summaryrefslogtreecommitdiffstats
path: root/src/AFC.c
diff options
context:
space:
mode:
authorGravatar Jonathan Beck2008-12-13 12:21:03 +0100
committerGravatar Jonathan Beck2008-12-13 12:21:03 +0100
commit3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100 (patch)
tree1080d26eca01c885efb33f3f98821a981a25e8b4 /src/AFC.c
parent3d8ba053deeacd74e621469d3d45d1db38ee411a (diff)
downloadlibplist-3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100.tar.gz
libplist-3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100.tar.bz2
Fork libiphone and remove anything non plist specific.
Update library and make related files acordingly .
Diffstat (limited to 'src/AFC.c')
-rw-r--r--src/AFC.c1018
1 files changed, 0 insertions, 1018 deletions
diff --git a/src/AFC.c b/src/AFC.c
deleted file mode 100644
index 899bd47..0000000
--- a/src/AFC.c
+++ /dev/null
@@ -1,1018 +0,0 @@
1/*
2 * AFC.c
3 * Contains functions for the built-in AFC client.
4 *
5 * Copyright (c) 2008 Zach C. All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <stdio.h>
23#include "AFC.h"
24#include "plist.h"
25
26
27// This is the maximum size an AFC data packet can be
28const int MAXIMUM_PACKET_SIZE = (2 << 15) - 32;
29
30/** Locks an AFC client, done for thread safety stuff
31 *
32 * @param client The AFC client connection to lock
33 */
34static void afc_lock(iphone_afc_client_t client)
35{
36 log_debug_msg("Locked\n");
37 /*while (client->lock) {
38 usleep(500); // they say it's obsolete, but whatever
39 }
40 client->lock = 1; */
41 g_mutex_lock(client->mutex);
42}
43
44/** Unlocks an AFC client, done for thread safety stuff.
45 *
46 * @param client The AFC
47 */
48static void afc_unlock(iphone_afc_client_t client)
49{ // just to be pretty
50 log_debug_msg("Unlocked\n");
51 //client->lock = 0;
52 g_mutex_unlock(client->mutex);
53}
54
55/** Makes a connection to the AFC service on the phone.
56 *
57 * @param phone The iPhone to connect on.
58 * @param s_port The source port.
59 * @param d_port The destination port.
60 *
61 * @return A handle to the newly-connected client or NULL upon error.
62 */
63iphone_error_t iphone_afc_new_client(iphone_device_t device, int src_port, int dst_port, iphone_afc_client_t * client)
64{
65 int ret = IPHONE_E_SUCCESS;
66
67 //makes sure thread environment is available
68 if (!g_thread_supported())
69 g_thread_init(NULL);
70 iphone_afc_client_t client_loc = (iphone_afc_client_t) malloc(sizeof(struct iphone_afc_client_int));
71
72 if (!device)
73 return IPHONE_E_INVALID_ARG;
74
75 // Attempt connection
76 client_loc->connection = NULL;
77 ret = iphone_mux_new_client(device, src_port, dst_port, &client_loc->connection);
78 if (IPHONE_E_SUCCESS != ret || !client_loc->connection) {
79 free(client_loc);
80 return ret;
81 }
82 // Allocate a packet
83 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket));
84 if (!client_loc->afc_packet) {
85 iphone_mux_free_client(client_loc->connection);
86 free(client_loc);
87 return IPHONE_E_UNKNOWN_ERROR;
88 }
89
90 client_loc->afc_packet->packet_num = 0;
91 client_loc->afc_packet->unknown1 = 0;
92 client_loc->afc_packet->unknown2 = 0;
93 client_loc->afc_packet->unknown3 = 0;
94 client_loc->afc_packet->unknown4 = 0;
95 client_loc->afc_packet->entire_length = 0;
96 client_loc->afc_packet->this_length = 0;
97 client_loc->afc_packet->header1 = 0x36414643;
98 client_loc->afc_packet->header2 = 0x4141504C;
99 client_loc->file_handle = 0;
100 client_loc->lock = 0;
101 client_loc->mutex = g_mutex_new();
102
103 *client = client_loc;
104 return IPHONE_E_SUCCESS;
105}
106
107/** Disconnects an AFC client from the phone.
108 *
109 * @param client The client to disconnect.
110 */
111iphone_error_t iphone_afc_free_client(iphone_afc_client_t client)
112{
113 if (!client || !client->connection || !client->afc_packet)
114 return IPHONE_E_INVALID_ARG;
115
116 iphone_mux_free_client(client->connection);
117 free(client->afc_packet);
118 free(client);
119 return IPHONE_E_SUCCESS;
120}
121
122
123/** Dispatches an AFC packet over a client.
124 *
125 * @param client The client to send data through.
126 * @param data The data to send.
127 * @param length The length to send.
128 *
129 * @return The number of bytes actually sent, or -1 on error.
130 *
131 * @warning set client->afc_packet->this_length and
132 * client->afc_packet->entire_length to 0 before calling this. The
133 * reason is that if you set them to different values, it indicates
134 * you want to send the data as two packets.
135 */
136static int dispatch_AFC_packet(iphone_afc_client_t client, const char *data, int length)
137{
138 int bytes = 0, offset = 0;
139 char *buffer;
140
141 if (!client || !client->connection || !client->afc_packet)
142 return 0;
143 if (!data || !length)
144 length = 0;
145
146 client->afc_packet->packet_num++;
147 if (!client->afc_packet->entire_length) {
148 client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length + 1 : sizeof(AFCPacket);
149 client->afc_packet->this_length = client->afc_packet->entire_length;
150 }
151 if (!client->afc_packet->this_length) {
152 client->afc_packet->this_length = sizeof(AFCPacket);
153 }
154 // We want to send two segments; buffer+sizeof(AFCPacket) to
155 // this_length is the parameters
156 // And everything beyond that is the next packet. (for writing)
157 if (client->afc_packet->this_length != client->afc_packet->entire_length) {
158 buffer = (char *) malloc(client->afc_packet->this_length);
159 memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket));
160 offset = client->afc_packet->this_length - sizeof(AFCPacket);
161
162 log_debug_msg("dispatch_AFC_packet: Offset: %i\n", offset);
163 if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) {
164 log_debug_msg("dispatch_AFC_packet: Length did not resemble what it was supposed");
165 log_debug_msg("to based on the packet.\n");
166 log_debug_msg("length minus offset: %i\n", length - offset);
167 log_debug_msg("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length);
168 free(buffer);
169 return -1;
170 }
171 memcpy(buffer + sizeof(AFCPacket), data, offset);
172 iphone_mux_send(client->connection, buffer, client->afc_packet->this_length, &bytes);
173 free(buffer);
174 if (bytes <= 0) {
175 return bytes;
176 }
177
178 log_debug_msg("dispatch_AFC_packet: sent the first now go with the second\n");
179 log_debug_msg("Length: %i\n", length - offset);
180 log_debug_msg("Buffer: \n");
181 log_debug_msg(data + offset);
182
183 iphone_mux_send(client->connection, data + offset, length - offset, &bytes);
184 return bytes;
185 } else {
186 log_debug_msg("dispatch_AFC_packet doin things the old way\n");
187 char *buffer = (char *) malloc(sizeof(char) * client->afc_packet->this_length);
188 log_debug_msg("dispatch_AFC_packet packet length = %i\n", client->afc_packet->this_length);
189 memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket));
190 log_debug_msg("dispatch_AFC_packet packet data follows\n");
191 if (length > 0) {
192 memcpy(buffer + sizeof(AFCPacket), data, length);
193 buffer[sizeof(AFCPacket) + length] = '\0';
194 }
195 log_debug_buffer(buffer, client->afc_packet->this_length);
196 log_debug_msg("\n");
197 iphone_mux_send(client->connection, buffer, client->afc_packet->this_length, &bytes);
198
199 if (buffer) {
200 free(buffer);
201 buffer = NULL;
202 }
203 return bytes;
204 }
205 return -1;
206}
207
208/** Receives data through an AFC client and sets a variable to the received data.
209 *
210 * @param client The client to receive data on.
211 * @param dump_here The char* to point to the newly-received data.
212 *
213 * @return How much data was received, 0 on successful receive with no errors,
214 * -1 if there was an error involved with receiving or if the packet
215 * received raised a non-trivial error condition (i.e. non-zero with
216 * AFC_ERROR operation)
217 */
218
219static int receive_AFC_data(iphone_afc_client_t client, char **dump_here)
220{
221 AFCPacket *r_packet;
222 char *buffer = (char *) malloc(sizeof(AFCPacket) * 4);
223 char *final_buffer = NULL;
224 int bytes = 0, recv_len = 0, current_count = 0;
225 int retval = 0;
226
227 iphone_mux_recv(client->connection, buffer, sizeof(AFCPacket) * 4, &bytes);
228 if (bytes <= 0) {
229 free(buffer);
230 fprintf(stderr, "Just didn't get enough.\n");
231 *dump_here = NULL;
232 return -1;
233 }
234
235 r_packet = (AFCPacket *) malloc(sizeof(AFCPacket));
236 memcpy(r_packet, buffer, sizeof(AFCPacket));
237
238 if (r_packet->entire_length == r_packet->this_length
239 && r_packet->entire_length > sizeof(AFCPacket) && r_packet->operation != AFC_ERROR) {
240 *dump_here = (char *) malloc(sizeof(char) * (r_packet->entire_length - sizeof(AFCPacket)));
241 memcpy(*dump_here, buffer + sizeof(AFCPacket), r_packet->entire_length - sizeof(AFCPacket));
242 retval = r_packet->entire_length - sizeof(AFCPacket);
243 free(buffer);
244 free(r_packet);
245 return retval;
246 }
247
248 uint32_t param1 = buffer[sizeof(AFCPacket)];
249 free(buffer);
250
251 if (r_packet->operation == AFC_ERROR && !(client->afc_packet->operation == AFC_DELETE && param1 == 7)) {
252 log_debug_msg("Oops? Bad operation code received: 0x%X, operation=0x%X, param1=%d\n",
253 r_packet->operation, client->afc_packet->operation, param1);
254 recv_len = r_packet->entire_length - r_packet->this_length;
255 free(r_packet);
256 log_debug_msg("recv_len=%d\n", recv_len);
257 if (param1 == 0) {
258 log_debug_msg("... false alarm, but still\n");
259 *dump_here = NULL;
260 return 0;
261 } else {
262 log_debug_msg("Errno %i\n", param1);
263 }
264 *dump_here = NULL;
265 return -1;
266 } else {
267 log_debug_msg("Operation code %x\nFull length %i and this length %i\n",
268 r_packet->operation, r_packet->entire_length, r_packet->this_length);
269 }
270
271 recv_len = r_packet->entire_length - r_packet->this_length;
272 free(r_packet);
273 if (!recv_len && r_packet->operation == AFC_SUCCESS_RESPONSE) {
274 *dump_here = NULL;
275 return 0;
276 }
277 // Keep collecting packets until we have received the entire file.
278 buffer = (char *) malloc(sizeof(char) * (recv_len < MAXIMUM_PACKET_SIZE) ? recv_len : MAXIMUM_PACKET_SIZE);
279 final_buffer = (char *) malloc(sizeof(char) * recv_len);
280 while (current_count < recv_len) {
281 iphone_mux_recv(client->connection, buffer, recv_len - current_count, &bytes);
282 log_debug_msg("receive_AFC_data: still collecting packets\n");
283 if (bytes < 0) {
284 log_debug_msg("receive_AFC_data: mux_recv failed: %d\n", bytes);
285 break;
286 }
287 if (bytes > recv_len - current_count) {
288 log_debug_msg("receive_AFC_data: mux_recv delivered too much data\n");
289 break;
290 }
291 if (bytes > 7 && strstr(buffer, "CFA6LPAA")) {
292 log_debug_msg("receive_AFC_data: WARNING: there is AFC data in this packet at %ti\n",
293 strstr(buffer, "CFA6LPAA") - buffer);
294 log_debug_msg("receive_AFC_data: the total packet length is %i\n", bytes);
295 }
296
297 memcpy(final_buffer + current_count, buffer, bytes);
298 current_count += bytes;
299 }
300 free(buffer);
301
302 *dump_here = final_buffer;
303 return current_count;
304}
305
306static int count_nullspaces(char *string, int number)
307{
308 int i = 0, nulls = 0;
309
310 for (i = 0; i < number; i++) {
311 if (string[i] == '\0')
312 nulls++;
313 }
314
315 return nulls;
316}
317
318static char **make_strings_list(char *tokens, int true_length)
319{
320 int nulls = 0, i = 0, j = 0;
321 char **list = NULL;
322
323 if (!tokens || !true_length)
324 return NULL;
325
326 nulls = count_nullspaces(tokens, true_length);
327 list = (char **) malloc(sizeof(char *) * (nulls + 1));
328 for (i = 0; i < nulls; i++) {
329 list[i] = strdup(tokens + j);
330 j += strlen(list[i]) + 1;
331 }
332 list[i] = NULL;
333
334 return list;
335}
336
337/** Gets a directory listing of the directory requested.
338 *
339 * @param client The client to get a directory listing from.
340 * @param dir The directory to list. (must be a fully-qualified path)
341 *
342 * @return A char ** list of files in that directory, terminated by an empty
343 * string for now or NULL if there was an error.
344 */
345iphone_error_t iphone_afc_get_dir_list(iphone_afc_client_t client, const char *dir, char ***list)
346{
347 int bytes = 0;
348 char *data = NULL, **list_loc = NULL;
349 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
350
351 if (!client || !dir || !list || (list && *list))
352 return IPHONE_E_INVALID_ARG;
353
354 afc_lock(client);
355
356 // Send the command
357 client->afc_packet->operation = AFC_LIST_DIR;
358 client->afc_packet->entire_length = 0;
359 client->afc_packet->this_length = 0;
360 bytes = dispatch_AFC_packet(client, dir, strlen(dir));
361 if (bytes <= 0) {
362 afc_unlock(client);
363 return IPHONE_E_NOT_ENOUGH_DATA;
364 }
365 // Receive the data
366 bytes = receive_AFC_data(client, &data);
367 if (bytes < 0 && !data) {
368 afc_unlock(client);
369 return IPHONE_E_NOT_ENOUGH_DATA;
370 }
371 // Parse the data
372 list_loc = make_strings_list(data, bytes);
373 if (list_loc)
374 ret = IPHONE_E_SUCCESS;
375 if (data)
376 free(data);
377
378 afc_unlock(client);
379 *list = list_loc;
380
381 return ret;
382}
383
384/** Get device info for a client connection to phone. (free space on disk, etc.)
385 *
386 * @param client The client to get device info for.
387 *
388 * @return A char ** list of parameters as given by AFC or NULL if there was an
389 * error.
390 */
391iphone_error_t iphone_afc_get_devinfo(iphone_afc_client_t client, char ***infos)
392{
393 int bytes = 0;
394 char *data = NULL, **list = NULL;
395
396 if (!client || !infos)
397 return IPHONE_E_INVALID_ARG;
398
399 afc_lock(client);
400
401 // Send the command
402 client->afc_packet->operation = AFC_GET_DEVINFO;
403 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
404 bytes = dispatch_AFC_packet(client, NULL, 0);
405 if (bytes < 0) {
406 afc_unlock(client);
407 return IPHONE_E_NOT_ENOUGH_DATA;
408 }
409 // Receive the data
410 bytes = receive_AFC_data(client, &data);
411 if (bytes < 0 && !data) {
412 afc_unlock(client);
413 return IPHONE_E_NOT_ENOUGH_DATA;
414 }
415 // Parse the data
416 list = make_strings_list(data, bytes);
417 if (data)
418 free(data);
419
420 afc_unlock(client);
421 *infos = list;
422 return IPHONE_E_SUCCESS;
423}
424
425/** Deletes a file.
426 *
427 * @param client The client to have delete the file.
428 * @param path The file to delete. (must be a fully-qualified path)
429 *
430 * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG
431 * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise.
432 */
433iphone_error_t iphone_afc_delete_file(iphone_afc_client_t client, const char *path)
434{
435 char *response = NULL;
436 int bytes;
437
438 if (!client || !path || !client->afc_packet || !client->connection)
439 return IPHONE_E_INVALID_ARG;
440
441 afc_lock(client);
442
443 // Send command
444 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
445 client->afc_packet->operation = AFC_DELETE;
446 bytes = dispatch_AFC_packet(client, path, strlen(path));
447 if (bytes <= 0) {
448 afc_unlock(client);
449 return IPHONE_E_NOT_ENOUGH_DATA;
450 }
451 // Receive response
452 bytes = receive_AFC_data(client, &response);
453 if (response)
454 free(response);
455
456 afc_unlock(client);
457
458 if (bytes < 0) {
459 return IPHONE_E_NOT_ENOUGH_DATA;
460 } else {
461 return IPHONE_E_SUCCESS;
462 }
463}
464
465/** Renames a file on the phone.
466 *
467 * @param client The client to have rename the file.
468 * @param from The file to rename. (must be a fully-qualified path)
469 * @param to The new name of the file. (must also be a fully-qualified path)
470 *
471 * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG
472 * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise.
473 */
474iphone_error_t iphone_afc_rename_file(iphone_afc_client_t client, const char *from, const char *to)
475{
476 char *response = NULL;
477 char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t)));
478 int bytes = 0;
479
480 if (!client || !from || !to || !client->afc_packet || !client->connection)
481 return IPHONE_E_INVALID_ARG;
482
483 afc_lock(client);
484
485 // Send command
486 memcpy(send, from, strlen(from) + 1);
487 memcpy(send + strlen(from) + 1, to, strlen(to) + 1);
488 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
489 client->afc_packet->operation = AFC_RENAME;
490 bytes = dispatch_AFC_packet(client, send, strlen(to) + strlen(from) + 2);
491 free(send);
492 if (bytes <= 0) {
493 afc_unlock(client);
494 return IPHONE_E_NOT_ENOUGH_DATA;
495 }
496 // Receive response
497 bytes = receive_AFC_data(client, &response);
498 if (response)
499 free(response);
500
501 afc_unlock(client);
502
503 if (bytes < 0) {
504 return IPHONE_E_NOT_ENOUGH_DATA;
505 } else {
506 return IPHONE_E_SUCCESS;
507 }
508}
509
510/** Creates a directory on the phone.
511 *
512 * @param client The client to use to make a directory.
513 * @param dir The directory's path. (must be a fully-qualified path, I assume
514 * all other mkdir restrictions apply as well)
515 *
516 * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG
517 * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise.
518 */
519iphone_error_t iphone_afc_mkdir(iphone_afc_client_t client, const char *dir)
520{
521 int bytes = 0;
522 char *response = NULL;
523
524 if (!client)
525 return IPHONE_E_INVALID_ARG;
526
527 afc_lock(client);
528
529 // Send command
530 client->afc_packet->operation = AFC_MAKE_DIR;
531 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
532 bytes = dispatch_AFC_packet(client, dir, strlen(dir));
533 if (bytes <= 0) {
534 afc_unlock(client);
535 return IPHONE_E_NOT_ENOUGH_DATA;
536 }
537 // Receive response
538 bytes = receive_AFC_data(client, &response);
539 if (response)
540 free(response);
541
542 afc_unlock(client);
543
544 if (bytes < 0) {
545 return IPHONE_E_NOT_ENOUGH_DATA;
546 } else {
547 return IPHONE_E_SUCCESS;
548 }
549}
550
551/** Gets information about a specific file.
552 *
553 * @param client The client to use to get the information of the file.
554 * @param path The fully-qualified path to the file.
555 *
556 * @return A pointer to an AFCFile struct containing the information received,
557 * or NULL on failure.
558 */
559iphone_afc_file_t afc_get_file_info(iphone_afc_client_t client, const char *path)
560{
561 char *received, **list;
562 iphone_afc_file_t my_file;
563 int length, i = 0;
564
565 afc_lock(client);
566
567 // Send command
568 client->afc_packet->operation = AFC_GET_INFO;
569 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
570 dispatch_AFC_packet(client, path, strlen(path));
571
572 // Receive data
573 length = receive_AFC_data(client, &received);
574 if (received) {
575 list = make_strings_list(received, length);
576 free(received);
577 } else {
578 afc_unlock(client);
579 return NULL;
580 }
581
582 afc_unlock(client);
583
584 // Parse the data
585 if (list) {
586 my_file = (iphone_afc_file_t) malloc(sizeof(struct iphone_afc_file_int));
587 for (i = 0; list[i]; i++) {
588 if (!strcmp(list[i], "st_size")) {
589 my_file->size = atoi(list[i + 1]);
590 }
591
592 if (!strcmp(list[i], "st_blocks")) {
593 my_file->blocks = atoi(list[i + 1]);
594 }
595
596 if (!strcmp(list[i], "st_ifmt")) {
597 if (!strcmp(list[i + 1], "S_IFREG")) {
598 my_file->type = S_IFREG;
599 } else if (!strcmp(list[i + 1], "S_IFDIR")) {
600 my_file->type = S_IFDIR;
601 }
602 }
603 }
604 g_strfreev(list);
605 return my_file;
606 } else {
607 return NULL;
608 }
609}
610
611/** Gets information about a specific file.
612 *
613 * @param client The client to use to get the information of the file.
614 * @param path The fully-qualified path to the file
615 * @param stbuf output buffer where file information will be stored
616 *
617 * @return A pointer to an AFCFile struct containing the information received,
618 * or NULL on failure.
619 */
620iphone_error_t iphone_afc_get_file_attr(iphone_afc_client_t client, const char *filename, struct stat * stbuf)
621{
622
623 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
624 if (!client || !client->connection || !client->afc_packet || !stbuf)
625 return IPHONE_E_INVALID_ARG;
626
627 memset(stbuf, 0, sizeof(struct stat));
628 iphone_afc_file_t file = afc_get_file_info(client, filename);
629 if (!file) {
630 ret = IPHONE_E_NO_SUCH_FILE;
631 } else {
632 stbuf->st_mode = file->type | (S_ISDIR(file->type) ? 0755 : 0644);
633 stbuf->st_size = file->size;
634 stbuf->st_blksize = 2048; // FIXME: Is this the actual block
635 // size used on the iPhone?
636 stbuf->st_blocks = file->blocks;
637 stbuf->st_uid = getuid();
638 stbuf->st_gid = getgid();
639
640 ret = iphone_afc_close_file(client, file);
641 }
642 return ret;
643}
644
645/** Opens a file on the phone.
646 *
647 * @param client The client to use to open the file.
648 * @param filename The file to open. (must be a fully-qualified path)
649 * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or
650 * AFC_FILE_WRITE; the former lets you read and write,
651 * however, and the second one will *create* the file,
652 * destroying anything previously there.
653 *
654 * @return A pointer to an AFCFile struct containing the file information (as
655 * received by afc_get_file_info) as well as the handle to the file or
656 * NULL in the case of failure.
657 */
658iphone_error_t
659iphone_afc_open_file(iphone_afc_client_t client, const char *filename,
660 iphone_afc_file_mode_t file_mode, iphone_afc_file_t * file)
661{
662 iphone_afc_file_t file_loc = NULL;
663 uint32_t ag = 0;
664 int bytes = 0, length = 0;
665 char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1));
666
667 if (!client || !client->connection || !client->afc_packet)
668 return IPHONE_E_INVALID_ARG;
669
670 afc_lock(client);
671
672 // Send command
673 memcpy(data, &file_mode, 4);
674 memcpy(data + 4, &ag, 4);
675 memcpy(data + 8, filename, strlen(filename));
676 data[8 + strlen(filename)] = '\0';
677 client->afc_packet->operation = AFC_FILE_OPEN;
678 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
679 bytes = dispatch_AFC_packet(client, data, 8 + strlen(filename));
680 free(data);
681
682 if (bytes <= 0) {
683 log_debug_msg("afc_open_file: Didn't receive a response to the command\n");
684 afc_unlock(client);
685 return IPHONE_E_NOT_ENOUGH_DATA;
686 }
687 // Receive the data
688 length = receive_AFC_data(client, &data);
689 if (length > 0 && data) {
690 afc_unlock(client);
691
692 // Get the file info and return it
693 file_loc = afc_get_file_info(client, filename);
694 memcpy(&file_loc->filehandle, data, 4);
695 free(data);
696 *file = file_loc;
697 return IPHONE_E_SUCCESS;
698 } else {
699 log_debug_msg("afc_open_file: Didn't get any further data\n");
700 afc_unlock(client);
701 return IPHONE_E_NOT_ENOUGH_DATA;
702 }
703
704 afc_unlock(client);
705
706 return IPHONE_E_UNKNOWN_ERROR;
707}
708
709/** Attempts to the read the given number of bytes from the given file.
710 *
711 * @param client The relevant AFC client
712 * @param file The AFCFile to read from
713 * @param data The pointer to the memory region to store the read data
714 * @param length The number of bytes to read
715 *
716 * @return The number of bytes read if successful. If there was an error -1.
717 */
718iphone_error_t
719iphone_afc_read_file(iphone_afc_client_t client, iphone_afc_file_t file, char *data, int length, uint32_t * bytes)
720{
721 char *input = NULL;
722 int current_count = 0, bytes_loc = 0;
723 const int MAXIMUM_READ_SIZE = 1 << 16;
724
725 if (!client || !client->afc_packet || !client->connection || !file)
726 return IPHONE_E_INVALID_ARG;
727 log_debug_msg("afc_read_file called for length %i\n", length);
728
729 afc_lock(client);
730
731 // Looping here to get around the maximum amount of data that
732 // recieve_AFC_data can handle
733 while (current_count < length) {
734 log_debug_msg("afc_read_file: current count is %i but length is %i\n", current_count, length);
735
736 // Send the read command
737 AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket));
738 packet->unknown1 = packet->unknown2 = 0;
739 packet->filehandle = file->filehandle;
740 packet->size = ((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE;
741 client->afc_packet->operation = AFC_READ;
742 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
743 bytes_loc = dispatch_AFC_packet(client, (char *) packet, sizeof(AFCFilePacket));
744 free(packet);
745
746 if (bytes_loc <= 0) {
747 afc_unlock(client);
748 return IPHONE_E_NOT_ENOUGH_DATA;
749 }
750 // Receive the data
751 bytes_loc = receive_AFC_data(client, &input);
752 log_debug_msg("afc_read_file: bytes returned: %i\n", bytes_loc);
753 if (bytes_loc < 0) {
754 if (input)
755 free(input);
756 afc_unlock(client);
757 return IPHONE_E_NOT_ENOUGH_DATA;
758 } else if (bytes_loc == 0) {
759 if (input)
760 free(input);
761 afc_unlock(client);
762 *bytes = current_count;
763 return IPHONE_E_SUCCESS; // FIXME check that's actually a
764 // success
765 } else {
766 if (input) {
767 log_debug_msg("afc_read_file: %d\n", bytes_loc);
768 memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc);
769 free(input);
770 input = NULL;
771 current_count += (bytes_loc > length) ? length : bytes_loc;
772 }
773 }
774 }
775 log_debug_msg("afc_read_file: returning current_count as %i\n", current_count);
776
777 afc_unlock(client);
778 *bytes = current_count;
779 return IPHONE_E_SUCCESS;
780}
781
782/** Writes a given number of bytes to a file.
783 *
784 * @param client The client to use to write to the file.
785 * @param file A pointer to an AFCFile struct; serves as the file handle.
786 * @param data The data to write to the file.
787 * @param length How much data to write.
788 *
789 * @return The number of bytes written to the file, or a value less than 0 if
790 * none were written...
791 */
792iphone_error_t
793iphone_afc_write_file(iphone_afc_client_t client, iphone_afc_file_t file,
794 const char *data, int length, uint32_t * bytes)
795{
796 char *acknowledgement = NULL;
797 const int MAXIMUM_WRITE_SIZE = 1 << 15;
798 uint32_t zero = 0, bytes_loc = 0, segments = (length / MAXIMUM_WRITE_SIZE), current_count = 0, i = 0;
799 char *out_buffer = NULL;
800
801 if (!client || !client->afc_packet || !client->connection || !file || !bytes)
802 return IPHONE_E_INVALID_ARG;
803
804 afc_lock(client);
805
806 log_debug_msg("afc_write_file: Write length: %i\n", length);
807
808 // Divide the file into segments.
809 for (i = 0; i < segments; i++) {
810 // Send the segment
811 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
812 client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE;
813 client->afc_packet->operation = AFC_WRITE;
814 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
815 memcpy(out_buffer, (char *) &file->filehandle, sizeof(uint32_t));
816 memcpy(out_buffer + 4, (char *) &zero, sizeof(uint32_t));
817 memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE);
818 bytes_loc = dispatch_AFC_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8);
819 if (bytes_loc < 0) {
820 afc_unlock(client);
821 return IPHONE_E_NOT_ENOUGH_DATA;
822 }
823 free(out_buffer);
824 out_buffer = NULL;
825
826 current_count += bytes_loc;
827 bytes_loc = receive_AFC_data(client, &acknowledgement);
828 if (bytes_loc < 0) {
829 afc_unlock(client);
830 return IPHONE_E_NOT_ENOUGH_DATA;
831 }
832 }
833
834 // By this point, we should be at the end. i.e. the last segment that
835 // didn't get sent in the for loop
836 // this length is fine because it's always sizeof(AFCPacket) + 8, but
837 // to be sure we do it again
838 if (current_count == length) {
839 afc_unlock(client);
840 *bytes = current_count;
841 return IPHONE_E_SUCCESS;
842 }
843
844 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
845 client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count);
846 client->afc_packet->operation = AFC_WRITE;
847 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
848 memcpy(out_buffer, (char *) &file->filehandle, sizeof(uint32_t));
849 memcpy(out_buffer + 4, (char *) &zero, sizeof(uint32_t));
850 memcpy(out_buffer + 8, data + current_count, (length - current_count));
851 bytes_loc = dispatch_AFC_packet(client, out_buffer, (length - current_count) + 8);
852 free(out_buffer);
853 out_buffer = NULL;
854
855 current_count += bytes_loc;
856
857 if (bytes_loc <= 0) {
858 afc_unlock(client);
859 *bytes = current_count;
860 return IPHONE_E_SUCCESS;
861 }
862
863 zero = bytes_loc;
864 bytes_loc = receive_AFC_data(client, &acknowledgement);
865 afc_unlock(client);
866 if (bytes_loc < 0) {
867 log_debug_msg("afc_write_file: uh oh?\n");
868 }
869 *bytes = current_count;
870 return IPHONE_E_SUCCESS;
871}
872
873/** Closes a file on the phone.
874 *
875 * @param client The client to close the file with.
876 * @param file A pointer to an AFCFile struct containing the file handle of the
877 * file to close.
878 */
879iphone_error_t iphone_afc_close_file(iphone_afc_client_t client, iphone_afc_file_t file)
880{
881 if (!client || !file)
882 return IPHONE_E_INVALID_ARG;
883 char *buffer = malloc(sizeof(char) * 8);
884 uint32_t zero = 0;
885 int bytes = 0;
886
887 afc_lock(client);
888
889 log_debug_msg("afc_close_file: File handle %i\n", file->filehandle);
890
891 // Send command
892 memcpy(buffer, &file->filehandle, sizeof(uint32_t));
893 memcpy(buffer + sizeof(uint32_t), &zero, sizeof(zero));
894 client->afc_packet->operation = AFC_FILE_CLOSE;
895 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
896 bytes = dispatch_AFC_packet(client, buffer, sizeof(char) * 8);
897 free(buffer);
898 buffer = NULL;
899
900 // FIXME: Is this necesary?
901 // client->afc_packet->entire_length = client->afc_packet->this_length
902 // = 0;
903
904 if (bytes <= 0) {
905 afc_unlock(client);
906 return IPHONE_E_UNKNOWN_ERROR;
907 }
908 // Receive the response
909 bytes = receive_AFC_data(client, &buffer);
910 if (buffer)
911 free(buffer);
912 free(file);
913 afc_unlock(client);
914 return IPHONE_E_SUCCESS;
915}
916
917/** Seeks to a given position of a pre-opened file on the phone.
918 *
919 * @param client The client to use to seek to the position.
920 * @param file The file to seek to a position on.
921 * @param seekpos Where to seek to. If passed a negative value, this will seek
922 * from the end of the file.
923 *
924 * @return IPHONE_E_SUCCESS on success, IPHONE_E_NOT_ENOUGH_DATA on failure.
925 */
926iphone_error_t iphone_afc_seek_file(iphone_afc_client_t client, iphone_afc_file_t file, int seekpos)
927{
928 char *buffer = (char *) malloc(sizeof(char) * 24);
929 uint32_t seekto = 0, bytes = 0, zero = 0;
930
931 if (seekpos < 0)
932 seekpos = file->size - abs(seekpos);
933
934 afc_lock(client);
935
936 // Send the command
937 seekto = seekpos;
938 memcpy(buffer, &file->filehandle, sizeof(uint32_t)); // handle
939 memcpy(buffer + 4, &zero, sizeof(uint32_t)); // pad
940 memcpy(buffer + 8, &zero, sizeof(uint32_t)); // fromwhere
941 memcpy(buffer + 12, &zero, sizeof(uint32_t)); // pad
942 memcpy(buffer + 16, &seekto, sizeof(uint32_t)); // offset
943 memcpy(buffer + 20, &zero, sizeof(uint32_t)); // pad
944 client->afc_packet->operation = AFC_FILE_SEEK;
945 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
946 bytes = dispatch_AFC_packet(client, buffer, 23);
947 free(buffer);
948 buffer = NULL;
949
950 if (bytes <= 0) {
951 afc_unlock(client);
952 return IPHONE_E_NOT_ENOUGH_DATA;
953 }
954 // Receive response
955 bytes = receive_AFC_data(client, &buffer);
956 if (buffer)
957 free(buffer);
958
959 afc_unlock(client);
960
961 if (bytes >= 0) {
962 return IPHONE_E_SUCCESS;
963 } else {
964 return IPHONE_E_NOT_ENOUGH_DATA;
965 }
966}
967
968/** Sets the size of a file on the phone.
969 *
970 * @param client The client to use to set the file size.
971 * @param file The (pre-opened) file to set the size on.
972 * @param newsize The size to set the file to.
973 *
974 * @return 0 on success, -1 on failure.
975 *
976 * @note This function is more akin to ftruncate than truncate, and truncate
977 * calls would have to open the file before calling this, sadly.
978 */
979iphone_error_t iphone_afc_truncate_file(iphone_afc_client_t client, iphone_afc_file_t file, uint32_t newsize)
980{
981 char *buffer = (char *) malloc(sizeof(char) * 16);
982 uint32_t bytes = 0, zero = 0;
983
984 afc_lock(client);
985
986 // Send command
987 memcpy(buffer, &file->filehandle, sizeof(uint32_t)); // handle
988 memcpy(buffer + 4, &zero, sizeof(uint32_t)); // pad
989 memcpy(buffer + 8, &newsize, sizeof(uint32_t)); // newsize
990 memcpy(buffer + 12, &zero, 3); // pad
991 client->afc_packet->operation = AFC_FILE_TRUNCATE;
992 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
993 bytes = dispatch_AFC_packet(client, buffer, 15);
994 free(buffer);
995 buffer = NULL;
996
997 if (bytes <= 0) {
998 afc_unlock(client);
999 return IPHONE_E_NOT_ENOUGH_DATA;
1000 }
1001 // Receive response
1002 bytes = receive_AFC_data(client, &buffer);
1003 if (buffer)
1004 free(buffer);
1005
1006 afc_unlock(client);
1007
1008 if (bytes >= 0) {
1009 return IPHONE_E_SUCCESS;
1010 } else {
1011 return IPHONE_E_NOT_ENOUGH_DATA;
1012 }
1013}
1014
1015uint32_t iphone_afc_get_file_handle(iphone_afc_file_t file)
1016{
1017 return file->filehandle;
1018}