summaryrefslogtreecommitdiffstats
path: root/src/afc.c
diff options
context:
space:
mode:
authorGravatar Martin Szulecki2010-01-13 15:08:14 +0100
committerGravatar Martin Szulecki2010-01-13 15:08:14 +0100
commitc9e2217059f561f87cf8b6af5067505f827c7297 (patch)
treeb3e46b6ed89035f9ad0ee696cf46c57445cabbf7 /src/afc.c
parent0ea52d01b817e35e4d4fceb57c9267124e60dab3 (diff)
downloadlibimobiledevice-c9e2217059f561f87cf8b6af5067505f827c7297.tar.gz
libimobiledevice-c9e2217059f561f87cf8b6af5067505f827c7297.tar.bz2
Rename service implementation sources to lowercase for consistency
Diffstat (limited to 'src/afc.c')
-rw-r--r--src/afc.c1285
1 files changed, 1285 insertions, 0 deletions
diff --git a/src/afc.c b/src/afc.c
new file mode 100644
index 0000000..fc09287
--- /dev/null
+++ b/src/afc.c
@@ -0,0 +1,1285 @@
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 <stdlib.h>
24#include <unistd.h>
25
26#include "afc.h"
27#include "iphone.h"
28#include "debug.h"
29
30// This is the maximum size an AFC data packet can be
31static const int MAXIMUM_PACKET_SIZE = (2 << 15);
32
33/** Locks an AFC client, done for thread safety stuff
34 *
35 * @param client The AFC client connection to lock
36 */
37static void afc_lock(afc_client_t client)
38{
39 debug_info("Locked");
40 g_mutex_lock(client->mutex);
41}
42
43/** Unlocks an AFC client, done for thread safety stuff.
44 *
45 * @param client The AFC
46 */
47static void afc_unlock(afc_client_t client)
48{
49 debug_info("Unlocked");
50 g_mutex_unlock(client->mutex);
51}
52
53/** Makes a connection to the AFC service on the phone.
54 *
55 * @param device The device to connect to.
56 * @param port The destination port.
57 * @param client Pointer that will be set to a newly allocated afc_client_t
58 * upon successful return.
59 *
60 * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARGUMENT when device or port
61 * is invalid, AFC_E_MUX_ERROR when the connection failed, or AFC_E_NO_MEM
62 * when there's a memory allocation problem.
63 */
64afc_error_t afc_client_new(iphone_device_t device, uint16_t port, afc_client_t * client)
65{
66 /* makes sure thread environment is available */
67 if (!g_thread_supported())
68 g_thread_init(NULL);
69
70 if (!device || port==0)
71 return AFC_E_INVALID_ARGUMENT;
72
73 /* attempt connection */
74 iphone_connection_t connection = NULL;
75 if (iphone_device_connect(device, port, &connection) != IPHONE_E_SUCCESS) {
76 return AFC_E_MUX_ERROR;
77 }
78
79 afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_int));
80 client_loc->connection = connection;
81
82 /* allocate a packet */
83 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket));
84 if (!client_loc->afc_packet) {
85 iphone_device_disconnect(client_loc->connection);
86 free(client_loc);
87 return AFC_E_NO_MEM;
88 }
89
90 client_loc->afc_packet->packet_num = 0;
91 client_loc->afc_packet->entire_length = 0;
92 client_loc->afc_packet->this_length = 0;
93 memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN);
94 client_loc->file_handle = 0;
95 client_loc->lock = 0;
96 client_loc->mutex = g_mutex_new();
97
98 *client = client_loc;
99 return AFC_E_SUCCESS;
100}
101
102/** Disconnects an AFC client from the phone.
103 *
104 * @param client The client to disconnect.
105 */
106afc_error_t afc_client_free(afc_client_t client)
107{
108 if (!client || !client->connection || !client->afc_packet)
109 return AFC_E_INVALID_ARGUMENT;
110
111 iphone_device_disconnect(client->connection);
112 free(client->afc_packet);
113 if (client->mutex) {
114 g_mutex_free(client->mutex);
115 }
116 free(client);
117 return AFC_E_SUCCESS;
118}
119
120/** Dispatches an AFC packet over a client.
121 *
122 * @param client The client to send data through.
123 * @param data The data to send.
124 * @param length The length to send.
125 * @param bytes_sent The number of bytes actually sent.
126 *
127 * @return AFC_E_SUCCESS on success, or an AFC_E_* error value on error.
128 *
129 * @warning set client->afc_packet->this_length and
130 * client->afc_packet->entire_length to 0 before calling this. The
131 * reason is that if you set them to different values, it indicates
132 * you want to send the data as two packets.
133 */
134static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, uint32_t length, uint32_t *bytes_sent)
135{
136 uint32_t offset = 0;
137 uint32_t sent = 0;
138
139 if (!client || !client->connection || !client->afc_packet)
140 return AFC_E_INVALID_ARGUMENT;
141
142 *bytes_sent = 0;
143
144 if (!data || !length)
145 length = 0;
146
147 client->afc_packet->packet_num++;
148 if (!client->afc_packet->entire_length) {
149 client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket);
150 client->afc_packet->this_length = client->afc_packet->entire_length;
151 }
152 if (!client->afc_packet->this_length) {
153 client->afc_packet->this_length = sizeof(AFCPacket);
154 }
155 // We want to send two segments; buffer+sizeof(AFCPacket) to
156 // this_length is the parameters
157 // And everything beyond that is the next packet. (for writing)
158 if (client->afc_packet->this_length != client->afc_packet->entire_length) {
159 offset = client->afc_packet->this_length - sizeof(AFCPacket);
160
161 debug_info("Offset: %i", offset);
162 if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) {
163 debug_info("Length did not resemble what it was supposed to based on packet");
164 debug_info("length minus offset: %i", length - offset);
165 debug_info("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length);
166 return AFC_E_INTERNAL_ERROR;
167 }
168
169 /* send AFC packet header */
170 AFCPacket_to_LE(client->afc_packet);
171 sent = 0;
172 iphone_device_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent);
173 if (sent == 0) {
174 /* FIXME: should this be handled as success?! */
175 return AFC_E_SUCCESS;
176 }
177 *bytes_sent += sent;
178
179 /* send AFC packet data */
180 sent = 0;
181 iphone_device_send(client->connection, data, offset, &sent);
182 if (sent == 0) {
183 return AFC_E_SUCCESS;
184 }
185 *bytes_sent += sent;
186
187 debug_info("sent the first now go with the second");
188 debug_info("Length: %i", length - offset);
189 debug_info("Buffer: ");
190 debug_buffer(data + offset, length - offset);
191
192 sent = 0;
193 iphone_device_send(client->connection, data + offset, length - offset, &sent);
194
195 *bytes_sent = sent;
196 return AFC_E_SUCCESS;
197 } else {
198 debug_info("doin things the old way");
199 debug_info("packet length = %i", client->afc_packet->this_length);
200
201 debug_buffer((char*)client->afc_packet, sizeof(AFCPacket));
202
203 /* send AFC packet header */
204 AFCPacket_to_LE(client->afc_packet);
205 sent = 0;
206 iphone_device_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent);
207 if (sent == 0) {
208 return AFC_E_SUCCESS;
209 }
210 *bytes_sent += sent;
211 /* send AFC packet data (if there's data to send) */
212 if (length > 0) {
213 debug_info("packet data follows");
214
215 debug_buffer(data, length);
216 iphone_device_send(client->connection, data, length, &sent);
217 *bytes_sent += sent;
218 }
219 return AFC_E_SUCCESS;
220 }
221 return AFC_E_INTERNAL_ERROR;
222}
223
224/** Receives data through an AFC client and sets a variable to the received data.
225 *
226 * @param client The client to receive data on.
227 * @param dump_here The char* to point to the newly-received data.
228 * @param bytes_recv How much data was received.
229 *
230 * @return AFC_E_SUCCESS when data has been received, or an AFC_E_* error value
231 * when an error occured.
232 */
233static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint32_t *bytes_recv)
234{
235 AFCPacket header;
236 uint32_t entire_len = 0;
237 uint32_t this_len = 0;
238 uint32_t current_count = 0;
239 uint64_t param1 = -1;
240
241 *bytes_recv = 0;
242
243 /* first, read the AFC header */
244 iphone_device_recv(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv);
245 AFCPacket_from_LE(&header);
246 if (*bytes_recv == 0) {
247 debug_info("Just didn't get enough.");
248 *dump_here = NULL;
249 return AFC_E_MUX_ERROR;
250 } else if (*bytes_recv < sizeof(AFCPacket)) {
251 debug_info("Did not even get the AFCPacket header");
252 *dump_here = NULL;
253 return AFC_E_MUX_ERROR;
254 }
255
256 /* check if it's a valid AFC header */
257 if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) {
258 debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!");
259 }
260
261 /* check if it has the correct packet number */
262 if (header.packet_num != client->afc_packet->packet_num) {
263 /* otherwise print a warning but do not abort */
264 debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num);
265 *dump_here = NULL;
266 return AFC_E_OP_HEADER_INVALID;
267 }
268
269 /* then, read the attached packet */
270 if (header.this_length < sizeof(AFCPacket)) {
271 debug_info("Invalid AFCPacket header received!");
272 *dump_here = NULL;
273 return AFC_E_OP_HEADER_INVALID;
274 } else if ((header.this_length == header.entire_length)
275 && header.entire_length == sizeof(AFCPacket)) {
276 debug_info("Empty AFCPacket received!");
277 *dump_here = NULL;
278 *bytes_recv = 0;
279 if (header.operation == AFC_OP_DATA) {
280 return AFC_E_SUCCESS;
281 } else {
282 return AFC_E_IO_ERROR;
283 }
284 }
285
286 debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation);
287
288 entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket);
289 this_len = (uint32_t)header.this_length - sizeof(AFCPacket);
290
291 /* this is here as a check (perhaps a different upper limit is good?) */
292 if (entire_len > (uint32_t)MAXIMUM_PACKET_SIZE) {
293 fprintf(stderr, "%s: entire_len is larger than MAXIMUM_PACKET_SIZE, (%d > %d)!", __func__, entire_len, MAXIMUM_PACKET_SIZE);
294 }
295
296 *dump_here = (char*)malloc(entire_len);
297 if (this_len > 0) {
298 iphone_device_recv(client->connection, *dump_here, this_len, bytes_recv);
299 if (*bytes_recv <= 0) {
300 free(*dump_here);
301 *dump_here = NULL;
302 debug_info("Did not get packet contents!");
303 return AFC_E_NOT_ENOUGH_DATA;
304 } else if (*bytes_recv < this_len) {
305 free(*dump_here);
306 *dump_here = NULL;
307 debug_info("Could not receive this_len=%d bytes", this_len);
308 return AFC_E_NOT_ENOUGH_DATA;
309 }
310 }
311
312 current_count = this_len;
313
314 if (entire_len > this_len) {
315 while (current_count < entire_len) {
316 iphone_device_recv(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv);
317 if (*bytes_recv <= 0) {
318 debug_info("Error receiving data (recv returned %d)", *bytes_recv);
319 break;
320 }
321 current_count += *bytes_recv;
322 }
323 if (current_count < entire_len) {
324 debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len);
325 }
326 }
327
328 if (current_count >= sizeof(uint64_t)) {
329 param1 = *(uint64_t*)(*dump_here);
330 }
331
332 debug_info("packet data size = %i", current_count);
333 debug_info("packet data follows");
334 debug_buffer(*dump_here, current_count);
335
336 /* check operation types */
337 if (header.operation == AFC_OP_STATUS) {
338 /* status response */
339 debug_info("got a status response, code=%lld", param1);
340
341 if (param1 != AFC_E_SUCCESS) {
342 /* error status */
343 /* free buffer */
344 free(*dump_here);
345 *dump_here = NULL;
346 return (afc_error_t)param1;
347 }
348 } else if (header.operation == AFC_OP_DATA) {
349 /* data response */
350 debug_info("got a data response");
351 } else if (header.operation == AFC_OP_FILE_OPEN_RES) {
352 /* file handle response */
353 debug_info("got a file handle response, handle=%lld", param1);
354 } else if (header.operation == AFC_OP_FILE_TELL_RES) {
355 /* tell response */
356 debug_info("got a tell response, position=%lld", param1);
357 } else {
358 /* unknown operation code received */
359 free(*dump_here);
360 *dump_here = NULL;
361 *bytes_recv = 0;
362
363 debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1);
364 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1);
365
366 return AFC_E_OP_NOT_SUPPORTED;
367 }
368
369 *bytes_recv = current_count;
370 return AFC_E_SUCCESS;
371}
372
373static uint32_t count_nullspaces(char *string, uint32_t number)
374{
375 uint32_t i = 0, nulls = 0;
376
377 for (i = 0; i < number; i++) {
378 if (string[i] == '\0')
379 nulls++;
380 }
381
382 return nulls;
383}
384
385static char **make_strings_list(char *tokens, uint32_t true_length)
386{
387 uint32_t nulls = 0, i = 0, j = 0;
388 char **list = NULL;
389
390 if (!tokens || !true_length)
391 return NULL;
392
393 nulls = count_nullspaces(tokens, true_length);
394 list = (char **) malloc(sizeof(char *) * (nulls + 1));
395 for (i = 0; i < nulls; i++) {
396 list[i] = strdup(tokens + j);
397 j += strlen(list[i]) + 1;
398 }
399 list[i] = NULL;
400
401 return list;
402}
403
404/** Gets a directory listing of the directory requested.
405 *
406 * @param client The client to get a directory listing from.
407 * @param dir The directory to list. (must be a fully-qualified path)
408 *
409 * @return A char ** list of files in that directory, terminated by an empty
410 * string for now or NULL if there was an error.
411 */
412afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list)
413{
414 uint32_t bytes = 0;
415 char *data = NULL, **list_loc = NULL;
416 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
417
418 if (!client || !dir || !list || (list && *list))
419 return AFC_E_INVALID_ARGUMENT;
420
421 afc_lock(client);
422
423 // Send the command
424 client->afc_packet->operation = AFC_OP_READ_DIR;
425 client->afc_packet->entire_length = 0;
426 client->afc_packet->this_length = 0;
427 ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes);
428 if (ret != AFC_E_SUCCESS) {
429 afc_unlock(client);
430 return AFC_E_NOT_ENOUGH_DATA;
431 }
432 // Receive the data
433 ret = afc_receive_data(client, &data, &bytes);
434 if (ret != AFC_E_SUCCESS) {
435 afc_unlock(client);
436 return ret;
437 }
438 // Parse the data
439 list_loc = make_strings_list(data, bytes);
440 if (data)
441 free(data);
442
443 afc_unlock(client);
444 *list = list_loc;
445
446 return ret;
447}
448
449/** Get device info for a client connection to phone. (free space on disk, etc.)
450 *
451 * @param client The client to get device info for.
452 *
453 * @return A char ** list of parameters as given by AFC or NULL if there was an
454 * error.
455 */
456afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
457{
458 uint32_t bytes = 0;
459 char *data = NULL, **list = NULL;
460 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
461
462 if (!client || !infos)
463 return AFC_E_INVALID_ARGUMENT;
464
465 afc_lock(client);
466
467 // Send the command
468 client->afc_packet->operation = AFC_OP_GET_DEVINFO;
469 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
470 ret = afc_dispatch_packet(client, NULL, 0, &bytes);
471 if (ret != AFC_E_SUCCESS) {
472 afc_unlock(client);
473 return AFC_E_NOT_ENOUGH_DATA;
474 }
475 // Receive the data
476 ret = afc_receive_data(client, &data, &bytes);
477 if (ret != AFC_E_SUCCESS) {
478 afc_unlock(client);
479 return ret;
480 }
481 // Parse the data
482 list = make_strings_list(data, bytes);
483 if (data)
484 free(data);
485
486 afc_unlock(client);
487
488 *infos = list;
489
490 return ret;
491}
492
493/** Get a specific key of the device info list for a client connection.
494 * Known key values are: Model, FSTotalBytes, FSFreeBytes and FSBlockSize.
495 * This is a helper function for afc_get_device_info().
496 *
497 * @param client The client to get device info for.
498 * @param key The key to get the value of.
499 * @param value The value for the key if successful or NULL otherwise.
500 *
501 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
502 */
503afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value)
504{
505 afc_error_t ret = AFC_E_INTERNAL_ERROR;
506 char **kvps, **ptr;
507
508 *value = NULL;
509 if (key == NULL)
510 return AFC_E_INVALID_ARGUMENT;
511
512 ret = afc_get_device_info(client, &kvps);
513 if (ret != AFC_E_SUCCESS)
514 return ret;
515
516 for (ptr = kvps; *ptr; ptr++) {
517 if (!strcmp(*ptr, key)) {
518 *value = strdup(*(ptr+1));
519 break;
520 }
521 }
522
523 g_strfreev(kvps);
524
525 return ret;
526}
527
528/** Deletes a file or directory.
529 *
530 * @param client The client to use.
531 * @param path The path to delete. (must be a fully-qualified path)
532 *
533 * @return AFC_E_SUCCESS if everythong went well, AFC_E_INVALID_ARGUMENT
534 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
535 */
536afc_error_t afc_remove_path(afc_client_t client, const char *path)
537{
538 char *response = NULL;
539 uint32_t bytes = 0;
540 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
541
542 if (!client || !path || !client->afc_packet || !client->connection)
543 return AFC_E_INVALID_ARGUMENT;
544
545 afc_lock(client);
546
547 // Send command
548 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
549 client->afc_packet->operation = AFC_OP_REMOVE_PATH;
550 ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes);
551 if (ret != AFC_E_SUCCESS) {
552 afc_unlock(client);
553 return AFC_E_NOT_ENOUGH_DATA;
554 }
555 // Receive response
556 ret = afc_receive_data(client, &response, &bytes);
557 if (response)
558 free(response);
559
560 /* special case; unknown error actually means directory not empty */
561 if (ret == AFC_E_UNKNOWN_ERROR)
562 ret = AFC_E_DIR_NOT_EMPTY;
563
564 afc_unlock(client);
565
566 return ret;
567}
568
569/** Renames a file or directory on the phone.
570 *
571 * @param client The client to have rename.
572 * @param from The name to rename from. (must be a fully-qualified path)
573 * @param to The new name. (must also be a fully-qualified path)
574 *
575 * @return AFC_E_SUCCESS if everythong went well, AFC_E_INVALID_ARGUMENT
576 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
577 */
578afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to)
579{
580 char *response = NULL;
581 char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t)));
582 uint32_t bytes = 0;
583 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
584
585 if (!client || !from || !to || !client->afc_packet || !client->connection)
586 return AFC_E_INVALID_ARGUMENT;
587
588 afc_lock(client);
589
590 // Send command
591 memcpy(send, from, strlen(from) + 1);
592 memcpy(send + strlen(from) + 1, to, strlen(to) + 1);
593 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
594 client->afc_packet->operation = AFC_OP_RENAME_PATH;
595 ret = afc_dispatch_packet(client, send, strlen(to)+1 + strlen(from)+1, &bytes);
596 free(send);
597 if (ret != AFC_E_SUCCESS) {
598 afc_unlock(client);
599 return AFC_E_NOT_ENOUGH_DATA;
600 }
601 // Receive response
602 ret = afc_receive_data(client, &response, &bytes);
603 if (response)
604 free(response);
605
606 afc_unlock(client);
607
608 return ret;
609}
610
611/** Creates a directory on the phone.
612 *
613 * @param client The client to use to make a directory.
614 * @param dir The directory's path. (must be a fully-qualified path, I assume
615 * all other mkdir restrictions apply as well)
616 *
617 * @return AFC_E_SUCCESS if everythong went well, AFC_E_INVALID_ARGUMENT
618 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
619 */
620afc_error_t afc_make_directory(afc_client_t client, const char *dir)
621{
622 uint32_t bytes = 0;
623 char *response = NULL;
624 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
625
626 if (!client)
627 return AFC_E_INVALID_ARGUMENT;
628
629 afc_lock(client);
630
631 // Send command
632 client->afc_packet->operation = AFC_OP_MAKE_DIR;
633 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
634 ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes);
635 if (ret != AFC_E_SUCCESS) {
636 afc_unlock(client);
637 return AFC_E_NOT_ENOUGH_DATA;
638 }
639 // Receive response
640 ret = afc_receive_data(client, &response, &bytes);
641 if (response)
642 free(response);
643
644 afc_unlock(client);
645
646 return ret;
647}
648
649/** Gets information about a specific file.
650 *
651 * @param client The client to use to get the information of the file.
652 * @param path The fully-qualified path to the file.
653 * @param infolist Pointer to a buffer that will be filled with a NULL-terminated
654 * list of strings with the file information.
655 * Set to NULL before calling this function.
656 *
657 * @return AFC_E_SUCCESS on success or an AFC_E_* error value
658 * when something went wrong.
659 */
660afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***infolist)
661{
662 char *received = NULL;
663 uint32_t bytes = 0;
664 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
665
666 if (!client || !path || !infolist)
667 return AFC_E_INVALID_ARGUMENT;
668
669 afc_lock(client);
670
671 // Send command
672 client->afc_packet->operation = AFC_OP_GET_FILE_INFO;
673 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
674 ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes);
675 if (ret != AFC_E_SUCCESS) {
676 afc_unlock(client);
677 return AFC_E_NOT_ENOUGH_DATA;
678 }
679
680 // Receive data
681 ret = afc_receive_data(client, &received, &bytes);
682 if (received) {
683 *infolist = make_strings_list(received, bytes);
684 free(received);
685 }
686
687 afc_unlock(client);
688
689 return ret;
690}
691
692/** Opens a file on the phone.
693 *
694 * @param client The client to use to open the file.
695 * @param filename The file to open. (must be a fully-qualified path)
696 * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or
697 * AFC_FILE_WRITE; the former lets you read and write,
698 * however, and the second one will *create* the file,
699 * destroying anything previously there.
700 * @param handle Pointer to a uint64_t that will hold the handle of the file
701 *
702 * @return AFC_E_SUCCESS on success or an AFC_E_* error on failure.
703 */
704iphone_error_t
705afc_file_open(afc_client_t client, const char *filename,
706 afc_file_mode_t file_mode, uint64_t *handle)
707{
708 uint64_t file_mode_loc = GUINT64_TO_LE(file_mode);
709 uint32_t bytes = 0;
710 char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1));
711 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
712
713 // set handle to 0 so in case an error occurs, the handle is invalid
714 *handle = 0;
715
716 if (!client || !client->connection || !client->afc_packet)
717 return AFC_E_INVALID_ARGUMENT;
718
719 afc_lock(client);
720
721 // Send command
722 memcpy(data, &file_mode_loc, 8);
723 memcpy(data + 8, filename, strlen(filename));
724 data[8 + strlen(filename)] = '\0';
725 client->afc_packet->operation = AFC_OP_FILE_OPEN;
726 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
727 ret = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1, &bytes);
728 free(data);
729
730 if (ret != AFC_E_SUCCESS) {
731 debug_info("Didn't receive a response to the command");
732 afc_unlock(client);
733 return AFC_E_NOT_ENOUGH_DATA;
734 }
735 // Receive the data
736 ret = afc_receive_data(client, &data, &bytes);
737 if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) {
738 afc_unlock(client);
739
740 // Get the file handle
741 memcpy(handle, data, sizeof(uint64_t));
742 free(data);
743 return ret;
744 }
745
746 debug_info("Didn't get any further data");
747
748 afc_unlock(client);
749
750 return ret;
751}
752
753/** Attempts to the read the given number of bytes from the given file.
754 *
755 * @param client The relevant AFC client
756 * @param handle File handle of a previously opened file
757 * @param data The pointer to the memory region to store the read data
758 * @param length The number of bytes to read
759 * @param bytes_read The number of bytes actually read.
760 *
761 * @return AFC_E_SUCCESS on success or an AFC_E_* error value on error.
762 */
763iphone_error_t
764afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read)
765{
766 char *input = NULL;
767 uint32_t current_count = 0, bytes_loc = 0;
768 const uint32_t MAXIMUM_READ_SIZE = 1 << 16;
769 afc_error_t ret = AFC_E_SUCCESS;
770
771 if (!client || !client->afc_packet || !client->connection || handle == 0)
772 return AFC_E_INVALID_ARGUMENT;
773 debug_info("called for length %i", length);
774
775 afc_lock(client);
776
777 // Looping here to get around the maximum amount of data that
778 // afc_receive_data can handle
779 while (current_count < length) {
780 debug_info("current count is %i but length is %i", current_count, length);
781
782 // Send the read command
783 AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket));
784 packet->filehandle = handle;
785 packet->size = GUINT64_TO_LE(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE);
786 client->afc_packet->operation = AFC_OP_READ;
787 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
788 ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc);
789 free(packet);
790
791 if (ret != AFC_E_SUCCESS) {
792 afc_unlock(client);
793 return AFC_E_NOT_ENOUGH_DATA;
794 }
795 // Receive the data
796 ret = afc_receive_data(client, &input, &bytes_loc);
797 debug_info("afc_receive_data returned error: %d", ret);
798 debug_info("bytes returned: %i", bytes_loc);
799 if (ret != AFC_E_SUCCESS) {
800 afc_unlock(client);
801 return ret;
802 } else if (bytes_loc == 0) {
803 if (input)
804 free(input);
805 afc_unlock(client);
806 *bytes_read = current_count;
807 /* FIXME: check that's actually a success */
808 return ret;
809 } else {
810 if (input) {
811 debug_info("%d", bytes_loc);
812 memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc);
813 free(input);
814 input = NULL;
815 current_count += (bytes_loc > length) ? length : bytes_loc;
816 }
817 }
818 }
819 debug_info("returning current_count as %i", current_count);
820
821 afc_unlock(client);
822 *bytes_read = current_count;
823 return ret;
824}
825
826/** Writes a given number of bytes to a file.
827 *
828 * @param client The client to use to write to the file.
829 * @param handle File handle of previously opened file.
830 * @param data The data to write to the file.
831 * @param length How much data to write.
832 * @param bytes_written The number of bytes actually written to the file.
833 *
834 * @return AFC_E_SUCCESS on success, or an AFC_E_* error value on error.
835 */
836iphone_error_t
837afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written)
838{
839 char *acknowledgement = NULL;
840 const uint32_t MAXIMUM_WRITE_SIZE = 1 << 15;
841 uint32_t current_count = 0, i = 0;
842 uint32_t segments = (length / MAXIMUM_WRITE_SIZE);
843 uint32_t bytes_loc = 0;
844 char *out_buffer = NULL;
845 afc_error_t ret = AFC_E_SUCCESS;
846
847 if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0))
848 return AFC_E_INVALID_ARGUMENT;
849
850 afc_lock(client);
851
852 debug_info("Write length: %i", length);
853
854 // Divide the file into segments.
855 for (i = 0; i < segments; i++) {
856 // Send the segment
857 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
858 client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE;
859 client->afc_packet->operation = AFC_OP_WRITE;
860 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
861 memcpy(out_buffer, (char *)&handle, sizeof(uint64_t));
862 memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE);
863 ret = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8, &bytes_loc);
864 if (ret != AFC_E_SUCCESS) {
865 afc_unlock(client);
866 return AFC_E_NOT_ENOUGH_DATA;
867 }
868 free(out_buffer);
869 out_buffer = NULL;
870
871 current_count += bytes_loc;
872 ret = afc_receive_data(client, &acknowledgement, &bytes_loc);
873 if (ret != AFC_E_SUCCESS) {
874 afc_unlock(client);
875 return ret;
876 } else {
877 free(acknowledgement);
878 }
879 }
880
881 // By this point, we should be at the end. i.e. the last segment that
882 // didn't get sent in the for loop
883 // this length is fine because it's always sizeof(AFCPacket) + 8, but
884 // to be sure we do it again
885 if (current_count == length) {
886 afc_unlock(client);
887 *bytes_written = current_count;
888 return ret;
889 }
890
891 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
892 client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count);
893 client->afc_packet->operation = AFC_OP_WRITE;
894 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
895 memcpy(out_buffer, (char *) &handle, sizeof(uint64_t));
896 memcpy(out_buffer + 8, data + current_count, (length - current_count));
897 ret = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8, &bytes_loc);
898 free(out_buffer);
899 out_buffer = NULL;
900
901 current_count += bytes_loc;
902
903 if (ret != AFC_E_SUCCESS) {
904 afc_unlock(client);
905 *bytes_written = current_count;
906 return AFC_E_SUCCESS;
907 }
908
909 ret = afc_receive_data(client, &acknowledgement, &bytes_loc);
910 afc_unlock(client);
911 if (ret != AFC_E_SUCCESS) {
912 debug_info("uh oh?");
913 } else {
914 free(acknowledgement);
915 }
916 *bytes_written = current_count;
917 return ret;
918}
919
920/** Closes a file on the phone.
921 *
922 * @param client The client to close the file with.
923 * @param handle File handle of a previously opened file.
924 */
925afc_error_t afc_file_close(afc_client_t client, uint64_t handle)
926{
927 char *buffer = malloc(sizeof(char) * 8);
928 uint32_t bytes = 0;
929 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
930
931 if (!client || (handle == 0))
932 return AFC_E_INVALID_ARGUMENT;
933
934 afc_lock(client);
935
936 debug_info("File handle %i", handle);
937
938 // Send command
939 memcpy(buffer, &handle, sizeof(uint64_t));
940 client->afc_packet->operation = AFC_OP_FILE_CLOSE;
941 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
942 ret = afc_dispatch_packet(client, buffer, 8, &bytes);
943 free(buffer);
944 buffer = NULL;
945
946 if (ret != AFC_E_SUCCESS) {
947 afc_unlock(client);
948 return AFC_E_UNKNOWN_ERROR;
949 }
950
951 // Receive the response
952 ret = afc_receive_data(client, &buffer, &bytes);
953 if (buffer)
954 free(buffer);
955
956 afc_unlock(client);
957
958 return ret;
959}
960
961/** Locks or unlocks a file on the phone.
962 *
963 * makes use of flock on the device, see
964 * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man2/flock.2.html
965 *
966 * @param client The client to lock the file with.
967 * @param handle File handle of a previously opened file.
968 * @param operation the lock or unlock operation to perform, this is one of
969 * AFC_LOCK_SH (shared lock), AFC_LOCK_EX (exclusive lock),
970 * or AFC_LOCK_UN (unlock).
971 */
972afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation)
973{
974 char *buffer = malloc(16);
975 uint32_t bytes = 0;
976 uint64_t op = GUINT64_TO_LE(operation);
977 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
978
979 if (!client || (handle == 0))
980 return AFC_E_INVALID_ARGUMENT;
981
982 afc_lock(client);
983
984 debug_info("file handle %i", handle);
985
986 // Send command
987 memcpy(buffer, &handle, sizeof(uint64_t));
988 memcpy(buffer + 8, &op, 8);
989
990 client->afc_packet->operation = AFC_OP_FILE_LOCK;
991 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
992 ret = afc_dispatch_packet(client, buffer, 16, &bytes);
993 free(buffer);
994 buffer = NULL;
995
996 if (ret != AFC_E_SUCCESS) {
997 afc_unlock(client);
998 debug_info("could not send lock command");
999 return AFC_E_UNKNOWN_ERROR;
1000 }
1001 // Receive the response
1002 ret = afc_receive_data(client, &buffer, &bytes);
1003 if (buffer) {
1004 debug_buffer(buffer, bytes);
1005 free(buffer);
1006 }
1007 afc_unlock(client);
1008
1009 return ret;
1010}
1011
1012/** Seeks to a given position of a pre-opened file on the phone.
1013 *
1014 * @param client The client to use to seek to the position.
1015 * @param handle File handle of a previously opened.
1016 * @param offset Seek offset.
1017 * @param whence Seeking direction, one of SEEK_SET, SEEK_CUR, or SEEK_END.
1018 *
1019 * @return AFC_E_SUCCESS on success, AFC_E_NOT_ENOUGH_DATA on failure.
1020 */
1021afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence)
1022{
1023 char *buffer = (char *) malloc(sizeof(char) * 24);
1024 int64_t offset_loc = (int64_t)GUINT64_TO_LE(offset);
1025 uint64_t whence_loc = GUINT64_TO_LE(whence);
1026 uint32_t bytes = 0;
1027 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1028
1029 if (!client || (handle == 0))
1030 return AFC_E_INVALID_ARGUMENT;
1031
1032 afc_lock(client);
1033
1034 // Send the command
1035 memcpy(buffer, &handle, sizeof(uint64_t)); // handle
1036 memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); // fromwhere
1037 memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); // offset
1038 client->afc_packet->operation = AFC_OP_FILE_SEEK;
1039 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
1040 ret = afc_dispatch_packet(client, buffer, 24, &bytes);
1041 free(buffer);
1042 buffer = NULL;
1043
1044 if (ret != AFC_E_SUCCESS) {
1045 afc_unlock(client);
1046 return AFC_E_NOT_ENOUGH_DATA;
1047 }
1048 // Receive response
1049 ret = afc_receive_data(client, &buffer, &bytes);
1050 if (buffer)
1051 free(buffer);
1052
1053 afc_unlock(client);
1054
1055 return ret;
1056}
1057
1058/** Returns current position in a pre-opened file on the phone.
1059 *
1060 * @param client The client to use.
1061 * @param handle File handle of a previously opened file.
1062 * @param position Position in bytes of indicator
1063 *
1064 * @return AFC_E_SUCCESS on success, AFC_E_NOT_ENOUGH_DATA on failure.
1065 */
1066afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position)
1067{
1068 char *buffer = (char *) malloc(sizeof(char) * 8);
1069 uint32_t bytes = 0;
1070 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1071
1072 if (!client || (handle == 0))
1073 return AFC_E_INVALID_ARGUMENT;
1074
1075 afc_lock(client);
1076
1077 // Send the command
1078 memcpy(buffer, &handle, sizeof(uint64_t)); // handle
1079 client->afc_packet->operation = AFC_OP_FILE_TELL;
1080 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
1081 ret = afc_dispatch_packet(client, buffer, 8, &bytes);
1082 free(buffer);
1083 buffer = NULL;
1084
1085 if (ret != AFC_E_SUCCESS) {
1086 afc_unlock(client);
1087 return AFC_E_NOT_ENOUGH_DATA;
1088 }
1089
1090 // Receive the data
1091 ret = afc_receive_data(client, &buffer, &bytes);
1092 if (bytes > 0 && buffer) {
1093 /* Get the position */
1094 memcpy(position, buffer, sizeof(uint64_t));
1095 *position = GUINT64_FROM_LE(*position);
1096 }
1097 if (buffer)
1098 free(buffer);
1099
1100 afc_unlock(client);
1101
1102 return ret;
1103}
1104
1105/** Sets the size of a file on the phone.
1106 *
1107 * @param client The client to use to set the file size.
1108 * @param handle File handle of a previously opened file.
1109 * @param newsize The size to set the file to.
1110 *
1111 * @return 0 on success, -1 on failure.
1112 *
1113 * @note This function is more akin to ftruncate than truncate, and truncate
1114 * calls would have to open the file before calling this, sadly.
1115 */
1116afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize)
1117{
1118 char *buffer = (char *) malloc(sizeof(char) * 16);
1119 uint32_t bytes = 0;
1120 uint64_t newsize_loc = GUINT64_TO_LE(newsize);
1121 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1122
1123 if (!client || (handle == 0))
1124 return AFC_E_INVALID_ARGUMENT;
1125
1126 afc_lock(client);
1127
1128 // Send command
1129 memcpy(buffer, &handle, sizeof(uint64_t)); // handle
1130 memcpy(buffer + 8, &newsize_loc, sizeof(uint64_t)); // newsize
1131 client->afc_packet->operation = AFC_OP_FILE_SET_SIZE;
1132 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
1133 ret = afc_dispatch_packet(client, buffer, 16, &bytes);
1134 free(buffer);
1135 buffer = NULL;
1136
1137 if (ret != AFC_E_SUCCESS) {
1138 afc_unlock(client);
1139 return AFC_E_NOT_ENOUGH_DATA;
1140 }
1141 // Receive response
1142 ret = afc_receive_data(client, &buffer, &bytes);
1143 if (buffer)
1144 free(buffer);
1145
1146 afc_unlock(client);
1147
1148 return ret;
1149}
1150
1151/** Sets the size of a file on the phone without prior opening it.
1152 *
1153 * @param client The client to use to set the file size.
1154 * @param path The path of the file to be truncated.
1155 * @param newsize The size to set the file to.
1156 *
1157 * @return AFC_E_SUCCESS if everything went well, AFC_E_INVALID_ARGUMENT
1158 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
1159 */
1160afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize)
1161{
1162 char *response = NULL;
1163 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8));
1164 uint32_t bytes = 0;
1165 uint64_t size_requested = GUINT64_TO_LE(newsize);
1166 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1167
1168 if (!client || !path || !client->afc_packet || !client->connection)
1169 return AFC_E_INVALID_ARGUMENT;
1170
1171 afc_lock(client);
1172
1173 // Send command
1174 memcpy(send, &size_requested, 8);
1175 memcpy(send + 8, path, strlen(path) + 1);
1176 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1177 client->afc_packet->operation = AFC_OP_TRUNCATE;
1178 ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes);
1179 free(send);
1180 if (ret != AFC_E_SUCCESS) {
1181 afc_unlock(client);
1182 return AFC_E_NOT_ENOUGH_DATA;
1183 }
1184 // Receive response
1185 ret = afc_receive_data(client, &response, &bytes);
1186 if (response)
1187 free(response);
1188
1189 afc_unlock(client);
1190
1191 return ret;
1192}
1193
1194/** Creates a hard link or symbolic link on the device.
1195 *
1196 * @param client The client to use for making a link
1197 * @param type 1 = hard link, 2 = symlink
1198 * @param target The file to be linked.
1199 * @param linkname The name of link.
1200 *
1201 * @return AFC_E_SUCCESS if everything went well, AFC_E_INVALID_ARGUMENT
1202 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
1203 */
1204afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname)
1205{
1206 char *response = NULL;
1207 char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8));
1208 uint32_t bytes = 0;
1209 uint64_t type = GUINT64_TO_LE(linktype);
1210 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1211
1212 if (!client || !target || !linkname || !client->afc_packet || !client->connection)
1213 return AFC_E_INVALID_ARGUMENT;
1214
1215 afc_lock(client);
1216
1217 debug_info("link type: %lld", type);
1218 debug_info("target: %s, length:%d", target, strlen(target));
1219 debug_info("linkname: %s, length:%d", linkname, strlen(linkname));
1220
1221 // Send command
1222 memcpy(send, &type, 8);
1223 memcpy(send + 8, target, strlen(target) + 1);
1224 memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1);
1225 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1226 client->afc_packet->operation = AFC_OP_MAKE_LINK;
1227 ret = afc_dispatch_packet(client, send, 8 + strlen(linkname) + 1 + strlen(target) + 1, &bytes);
1228 free(send);
1229 if (ret != AFC_E_SUCCESS) {
1230 afc_unlock(client);
1231 return AFC_E_NOT_ENOUGH_DATA;
1232 }
1233 // Receive response
1234 ret = afc_receive_data(client, &response, &bytes);
1235 if (response)
1236 free(response);
1237
1238 afc_unlock(client);
1239
1240 return ret;
1241}
1242
1243/** Sets the modification time of a file on the phone.
1244 *
1245 * @param client The client to use to set the file size.
1246 * @param path Path of the file for which the modification time should be set.
1247 * @param mtime The modification time to set in nanoseconds since epoch.
1248 *
1249 * @return AFC_E_SUCCESS if everything went well, AFC_E_INVALID_ARGUMENT
1250 * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise.
1251 */
1252afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime)
1253{
1254 char *response = NULL;
1255 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8));
1256 uint32_t bytes = 0;
1257 uint64_t mtime_loc = GUINT64_TO_LE(mtime);
1258 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1259
1260 if (!client || !path || !client->afc_packet || !client->connection)
1261 return AFC_E_INVALID_ARGUMENT;
1262
1263 afc_lock(client);
1264
1265 // Send command
1266 memcpy(send, &mtime_loc, 8);
1267 memcpy(send + 8, path, strlen(path) + 1);
1268 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1269 client->afc_packet->operation = AFC_OP_SET_FILE_TIME;
1270 ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes);
1271 free(send);
1272 if (ret != AFC_E_SUCCESS) {
1273 afc_unlock(client);
1274 return AFC_E_NOT_ENOUGH_DATA;
1275 }
1276 // Receive response
1277 ret = afc_receive_data(client, &response, &bytes);
1278 if (response)
1279 free(response);
1280
1281 afc_unlock(client);
1282
1283 return ret;
1284}
1285