1/* check share_info in all partitions and update AppleVolumes.default and reload afpd.
2 *
3 *  Copyright (C) 2008 - 2009, Delta Networks, Inc.
4 *
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <errno.h>
11#include <string.h>
12#include <sys/time.h>
13#include <time.h>
14#include <signal.h>
15#include <ctype.h>
16
17/*
18  * The 'USB_Functionality_specification_v0.2.doc' is modified too much, so I don't want to
19  * touch the original code .... :)
20  */
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <sys/statfs.h>
24#include <unistd.h>
25#include <linux/magic.h>
26#include <sys/wait.h>
27
28#include "list.h"
29
30#if 0
31#define USB_DEBUGP(format, args...) printf(format, ## args)
32#else
33#define USB_DEBUGP(format, args...)
34#endif
35
36#define ITUNES_SHARE_FOLDER_SIZE 4096
37char itunes_share_floders[ITUNES_SHARE_FOLDER_SIZE] = {0};
38char itunes_db_folder[128] = {0};
39
40static const char daapd_conf[] =
41"general {\n"
42"	uid = \"root\"\n"
43"	logfile = \"%s/forked-daapd.log\"\n"
44"	db_path = \"%s/forked-daapd.db\"\n"
45"	loglevel = 5\n"
46"	admin_password = \"unused\"\n"
47"	ipv6 = false\n"
48"}\n"
49"library {\n"
50"	name = \"%s\"\n"
51"	port = 3689\n"
52"	directories = { \"/tmp/itunes\", %s }\n"
53"	remote_pairing_file = \"/tmp/itunes/apple.remote\"\n"
54"	itunes_overrides = false\n"
55"}\n"
56"audio {\n"
57"	nickname = \"R7800\"\n"
58"}";
59
60struct disk_partition_info
61{
62	struct list_head list;
63	int mounted;
64	int afplistupdated;
65	int ishfsplus;
66	char label;	/* `U` ~ ... */
67	char name[15]; /* `sda1` `sda2` */
68	char vendor[128];  /*device name :SigmaTel MSCN*/
69	char vol_name[31]; /* Volume Name */
70	char device_id[128]; /* serialNum_partitionNum */
71	unsigned long long capacity; /* capacity size in MB */
72};
73
74struct share_info
75{
76	struct list_head list;
77	char name[];
78};
79
80#define USB_APPLE_VOLUMES_DEFAULT_CONF	"/etc/netatalk/AppleVolumes.default"
81#define AVAHI_SERVICE_ADISK	"/etc/avahi/services/adisk.service"
82#define TMP_AFP_LOCK  "/tmp/tmp_afp_lock"
83#define SHARE_FILE_INFO "shared_usb_folder"
84#define APPROVED_DISK	"USB_approved_device"
85
86#define USER_ADMIN "admin"
87#define USER_GUEST "guest"
88#define USB_PATH_SIZE	4096
89
90extern char *config_get(char* name);
91extern int config_match(char *name, char *match);
92
93char SATA_DEV_NAME[32];
94char SATA_DEV_SERIAL_NO[128];
95char SD_CARD_DEV_NAME[32];
96
97static void reload_services(void)
98{
99	int ret;
100	/* directly return ,when disable the usb network for afp access */
101	/* FIXME: what is the use case??? */
102//	if (config_match("usb_enableNet", "1"))
103//		return;
104
105	/* Sync with locking file, and wait 1s to not miss SIGUP for `afpd` */
106	sleep(1);
107	ret = system("/bin/pidof afpd > /dev/zero 2>&1");
108	if (ret != -1 && WIFEXITED(ret) && WEXITSTATUS(ret) == 0) {
109		system("/bin/kill -HUP `cat /var/run/afpd.pid` > /dev/null 2>&1");
110	} else {
111		system("/bin/nice -n 19 /usr/sbin/afpd -F /etc/netatalk/afpd.conf -P /var/run/afpd.pid -c 7 > /dev/null 2>&1");
112	}
113
114#if 0 /* Not required */
115	/* cnid_metad */
116	ret = system("/bin/pidof cnid_metad > /dev/zero 2>&1");
117	if (ret != 0)
118		system("/usr/sbin/cnid_metad > /dev/zero 2>&1");
119#endif
120
121	/* avahi-daemon: not required */
122}
123
124static inline char *user_name(char *code)
125{
126	if (*code == '1')
127		return USER_ADMIN;
128	else
129		return USER_GUEST;
130}
131
132static void add_afpd_share_info(FILE *fp, char *displayname, char *reader, char *writer, char *path)
133{
134	fprintf(fp, "%s \"%s\"", path, displayname);
135
136	/* FIXME: set proper permission and/or allow proper user */
137	if (strncmp(reader, USER_GUEST, strlen(USER_GUEST)))
138		fprintf(fp, " allow:@admin deny:@guest");
139	else if (strncmp(writer, USER_GUEST, strlen(USER_GUEST)))
140		fprintf(fp, " allow:@admin,@guest rolist:@guest");
141	else
142		fprintf(fp, " allow:@admin,@guest");
143
144	fprintf(fp, " cnidscheme:cdb options:usedots,tm\n");
145}
146
147int is_sda(char * dev)
148{
149	int count = 0;
150	FILE *fp;
151	char part_name[16], line[128];
152	int major, minors;
153	unsigned long long capacity;
154
155	fp = fopen("/proc/partitions", "r");
156	if (fp == NULL)
157		goto ret;
158
159	/*
160	 *           * major minor  #blocks  name
161	 *           *
162	 *           *  31     0        320 mtdblock0
163	 *           * ....
164	 *           *   8     0    3968000 sda
165	 *           *   8     1    3963968 sda1
166	 *
167	 */
168
169	while (fgets(line, sizeof(line), fp)) {
170		if (sscanf(line, " %d %d %llu %[^\n ]",
171					&major, &minors, &capacity, part_name) != 4)
172			continue;
173		if (strncmp(part_name, dev, 3))
174			continue;
175		else
176			count++;
177	}
178
179ret:
180	if (fp != NULL)
181		fclose(fp);
182
183	return ((count == 1)?1:0);
184}
185
186static char *get_device_vendor(char *dev)
187{
188	int i,j;
189	FILE *fp;
190	char line[100];
191	static char vendor[128];
192	char path[64], *ven_mod[] = {"vendor", "model"};
193
194	vendor[0] = '\0';
195
196	for (i=0; i<2; i++){
197		snprintf(path, sizeof(path), "/sys/block/%s/device/%s", dev, ven_mod[i]);
198		if (!(fp = fopen(path, "r")))
199			continue;
200		fgets(line, sizeof(line), fp);
201		fclose(fp);
202
203		j = 0;
204		while (line[j] != '\0' && line[j] != '\r' && line[j] != '\n')
205			j++;
206		line[j] = '\0';
207
208		strcat(vendor, line);
209		strcat(vendor, " ");
210	}
211
212	j = 127;
213	while(vendor[j] == '\t' || vendor[j] == ' ' || vendor[j] == '\0')
214		j--;
215	vendor[j + 1] = '\0';
216
217	return vendor;
218}
219
220/*
221  * When presenting the Capacity of a device, the appropriate units should be used.
222  * If a device is 1GB in size this should be displayed as 1GB, however a 300MB device
223  * should be displayed as 300MB and not 0.3GB. (29.66GB, 44.53GB).
224  */
225static void format_capacity(char *buf, int buflen, unsigned long long megabytes)
226{
227	if (megabytes >= 1024) {
228		unsigned long long left = ((megabytes & 0x3FF) * 100) >> 10; // (leftMB / 1024) * 100
229		if (left == 0)
230			snprintf(buf, buflen, "%llu GB", (megabytes >> 10));
231		else
232			snprintf(buf, buflen, "%llu.%02llu GB", (megabytes >> 10), left);
233	} else {
234		snprintf(buf, buflen, "%llu MB", megabytes);
235	}
236}
237
238static int is_special_backup_format(char *dev)
239{
240#define DISK_FORMAT_FILE "/tmp/disk_special_format"
241	int ret = 0;
242	int i = 0;
243	FILE *fp;
244	char cmd[128], buf[256];
245
246	memset(cmd,0,128);
247	memset(buf,0,256);
248	snprintf(cmd, sizeof(cmd), "/bin/mount | grep '/mnt/%s' | awk '{print$5}' > " DISK_FORMAT_FILE, dev);
249	system(cmd);
250
251	if (!(fp = fopen(DISK_FORMAT_FILE, "r")))
252		return -1;
253
254	fgets(buf, sizeof(buf), fp);
255	fclose(fp);
256
257	i = 0;
258	while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
259		i++;
260	buf[i] = '\0';
261	if ( !strcmp(buf,"hfsplus") || !strcmp(buf, "xfs") || !strcmp(buf, "ntfs")
262			|| !strcmp(buf, "ext2") || !strcmp(buf, "ext3") || !strcmp(buf, "ext4") )
263		ret = 1;
264	//printf("dev = %s, buf = %s", dev, buf);
265	return ret;
266}
267
268static int is_hfsplus_formated(char *dev)
269{
270#define VOLUME_FORMAT_FILE "/tmp/disk_format"
271	int  ret = 0;
272	int  i = 0;
273	char cmd[128], buf[256];
274	FILE *fp;
275
276	memset(cmd,0,128);
277	memset(buf,0,256);
278
279	snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -t /dev/%s > " VOLUME_FORMAT_FILE, dev);
280	system(cmd);
281
282	if (!(fp = fopen(VOLUME_FORMAT_FILE, "r"))) {
283		return -1;
284	}
285
286	fgets(buf, sizeof(buf), fp);
287	fclose(fp);
288
289	i = 0;
290	while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
291		i++;
292	buf[i] = '\0';
293
294	if ( !strcmp(buf,"hfsplus") || !strcmp(buf, "xfs") || !strcmp(buf, "ntfs")
295			|| !strcmp(buf, "ext2") || !strcmp(buf, "ext3") || !strcmp(buf, "ext4") )
296		ret = 1;
297
298	USB_DEBUGP("\nDev: %s vol_type: %s ret: %d\n", dev, buf, ret);
299	return ret;
300}
301
302static char * get_usb_serial_num(char *part_name)
303{
304	static char serial_num[128];
305	char disk_name[8], path[64], line[128], cmd[256];
306	char *bus_num = NULL;
307	char *p;
308	int j = 0;
309	FILE *fp;
310
311	serial_num[0] = '\0';
312
313	snprintf(disk_name, 8, "%s", part_name);
314	disk_name[3] = '\0';
315	sprintf(cmd, "/bin/ls /sys/block/%s/device/scsi_device/ > /tmp/%s_info_tmp", disk_name, disk_name);
316	system(cmd);
317
318	sprintf(path, "/tmp/%s_info_tmp", disk_name);
319	fp= fopen(path, "r");
320	if (fp == NULL)
321		goto ret;
322	while (fgets(line, sizeof(line), fp)) {
323		line[15] = '\0';
324		char *tok = ":\n";
325		bus_num = strtok(line, tok);
326		fclose(fp);
327	}
328
329	if(bus_num != NULL){
330		snprintf(path, 64, "/proc/scsi/usb-storage/%s", bus_num);
331		fp= fopen(path, "r");
332		if (fp == NULL)
333			goto ret;
334
335		while (fgets(line, sizeof(line), fp)) {
336			if(strncmp(line, "Serial Number:", 14) == 0){
337				while (line[j] != '\r' && line[j] != '\n')
338					j++;
339				line[j] = '\0';
340
341				/* delete the space/tab at the end of the Serial Number */
342				p = line + 14;
343				while (*p != '\0')
344					++p;
345				--p;
346				while (*p == ' ' || *p == '\t')
347					--p;
348				*(p + 1) = '\0';
349				/* skip the space/tab at the head of the Serial Number */
350				p = line + 14;
351				while (*p == ' ' || *p == '\t')
352					p++;
353				strcpy(serial_num, p);
354			}
355		}
356	}
357
358ret:
359	if (fp != NULL)
360		fclose(fp);
361	return serial_num;
362}
363
364static char * get_sata_serial_num(void)
365{
366	return SATA_DEV_SERIAL_NO;
367}
368
369/* use filesystem uuid instead serial num of sd card */
370static char * get_sd_card_serial_num(char *part_name)
371{
372#define SD_CARD_UUID_FILE "/tmp/sd_card_uuid"
373	static char uuid[128];
374	char buf[128], cmd[64];
375	int i = 0;
376	FILE *fp;
377        buf[0] = '\0';
378
379     	snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -u /dev/%s > "SD_CARD_UUID_FILE, part_name);
380       	system(cmd);
381	if(!(fp = fopen(SD_CARD_UUID_FILE, "r")))
382               	return get_usb_serial_num(part_name);
383
384       	fgets(buf, sizeof(buf), fp);
385       	fclose(fp);
386
387       	while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
388               	i++;
389       	buf[i] = '\0';
390	if (i == 0)
391		return get_usb_serial_num(part_name);
392	strncpy(uuid, buf, sizeof(uuid));
393	return uuid;
394}
395
396static void  get_device_id(struct disk_partition_info *disk)
397{
398	char *id;
399	id = disk->device_id;
400	if(strncmp(disk->name, SATA_DEV_NAME, 3) == 0)
401		snprintf(id, sizeof(disk->device_id), "%s*%s", get_sata_serial_num(), disk->name + 3);
402	else if(strncmp(disk->name, SD_CARD_DEV_NAME, 3) == 0)
403		snprintf(id, sizeof(disk->device_id), "%s*%s", get_sd_card_serial_num(disk->name), disk->name + 3);
404	else
405		snprintf(id, sizeof(disk->device_id), "%s*%s", get_usb_serial_num(disk->name), disk->name + 3);
406
407}
408
409static void  get_disk_volume(struct disk_partition_info *disk, char *part_name)
410{
411//#define VOLUME_ID_FILE "/tmp/vol_id"
412/* FIX Bug 28761 - [USB]sometimes some drives appear as "Not Shared"
413   cause:
414       Module net-cgi and samba-script will read and write the
415       same file, sometimes it will cause conflict.
416   solution:
417       Use two tmp file to avoid the conflict.
418        Module net-cgi use "vol_id"
419        Module samba-script use "vol_id_1"
420*/
421#define VOLUME_ID_FILE "/tmp/vol_id_2"
422	int i;
423	FILE *fp;
424	char *buf, disknum[4], diskname[4], capacity[32], cmd[256];
425
426	buf = disk->vol_name;
427	buf[0] = '\0';
428
429	snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -L /dev/%s > " VOLUME_ID_FILE, part_name);
430	system(cmd);
431
432	if(!(fp = fopen(VOLUME_ID_FILE, "r"))){
433		printf("[get_disk_volume vol_id] open file vol_id_2 error!!\n");
434		fclose(fp);
435		goto ret;
436	}
437
438	fgets(buf, sizeof(disk->vol_name), fp);
439	fclose(fp);
440
441	i = 0;
442	while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
443		i++;
444	buf[i] = '\0';
445
446	if (buf[0] == '\0'){
447		//printf("[afp: get_disk_volume vol_id] Get info by vol_id failed!!\n");
448		memset(cmd,0,sizeof(cmd));
449		strncpy(diskname, part_name, 3);
450		diskname[3] = '\0';
451		disknum[0] = part_name[3];
452		disknum[1] = '\0';
453		snprintf(cmd, sizeof(cmd), "/bin/echo $\(/usr/sbin/parted -s /dev/%s print | grep \"Number\" -A16 | sed -n '2,16p' | awk 'NF>=6{for(n=6;n<=NF;n++)printf $n\" \";print \"\"}' | sed -n %dp) > "  VOLUME_ID_FILE, diskname, atoi(disknum));
454		system(cmd);
455		if(!(fp = fopen(VOLUME_ID_FILE, "r"))){
456        		printf("[get_disk_volume parted] open file vol_id_2 error!!\n");
457	        	fclose(fp);
458        		goto ret;
459		}
460		fgets(buf, sizeof(disk->vol_name), fp);
461		fclose(fp);
462
463		i = 0;
464		while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
465        		i++;
466		buf[i] = '\0';
467	}
468
469ret:
470	/*
471	 * If Volume Name is empty, then use <USB Device Letter> Drive (<Capacity>)
472	 * e.g U Drive (512MB)
473	 */
474
475	format_capacity(capacity, sizeof(capacity), disk->capacity);
476	if (buf[0] == '\0') {
477		printf("[vol_id_2]get_disk_volume error, goto ret!!!\n");
478		if(strncmp(part_name, SATA_DEV_NAME, 3) == 0) {
479                	snprintf(buf, sizeof(disk->vol_name), "%c External_Disk(%s)", disk->label, capacity);
480	        }else if(strncmp(part_name, SD_CARD_DEV_NAME, 3) == 0){
481                	snprintf(buf, sizeof(disk->vol_name), "%c Sd_Card (%s)", disk->label, capacity);
482		}else{
483                	snprintf(buf, sizeof(disk->vol_name), "%c Drive (%s)", disk->label, capacity);
484		}
485	}
486}
487
488static int is_loop_partition(char *dev)
489{
490#define DISK_LOOP_PARTITION "/tmp/disk_loop_pt"
491       int  ret = 0;
492       int  i = 0;
493       char diskname[4], cmd[128], buf[256];
494       FILE *fp;
495
496       memset(cmd,0,128);
497       memset(buf,0,256);
498
499       strncpy(diskname, dev, 3);
500       diskname[3] = '\0';
501       snprintf(cmd, sizeof(cmd), "/usr/sbin/parted -s /dev/%s print | grep \"Partition Table\" | awk '{print $3}' > " DISK_LOOP_PARTITION, diskname);
502       system(cmd);
503       if (!(fp = fopen(DISK_LOOP_PARTITION, "r"))) {
504               fclose(fp);
505               return 0;
506       }
507       fgets(buf, sizeof(buf), fp);
508       fclose(fp);
509
510       i = 0;
511       while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n')
512               i++;
513       buf[i] = '\0';
514
515       if ( !strcmp(buf,"loop") ){
516               fprintf(stderr, "[is_loop_partition]: Disk %s is a loop partition!\n", dev);
517               ret = 1;
518       }
519
520       return ret;
521}
522
523static int is_noshare_partition(char *dev)
524{
525	FILE *fp;
526	char diskname[4], cmd[128], result[8];
527
528	if (strlen(dev) != 4)
529		return 0;
530
531	strncpy(diskname, dev, 3);
532	diskname[3] = '\0';
533
534	snprintf(cmd, sizeof(cmd), "/usr/sbin/parted -s /dev/%s print noshare | grep %s", diskname, dev);
535	fp = popen(cmd, "r");
536	if (!fp) {
537		perror("popen");
538		return 0;
539	}
540
541	memset(result, 0, sizeof(result));
542	fgets(result, sizeof(result), fp);
543
544	pclose(fp);
545
546	return strlen(result) >= 4 ? 1 : 0;
547}
548
549static void scan_disk_entries(struct list_head *head)
550{
551	FILE *fp;
552	struct statfs statbuf;
553	int i = 0, j=0, k = 0, major,minors;
554	int have_disk_mouted = 0, have_hfsplus_disk_mounted = 0;
555	unsigned long long capacity;
556	char mnt_path[32],*vendor = NULL;
557	char *s, part_name[128], line[256];
558	struct disk_partition_info *partinfo;
559
560	fp = fopen("/proc/partitions","r");
561	if (fp == NULL )
562		return;
563
564	/*
565	  * major minor  #blocks  name
566	  *
567	  *  31     0        320 mtdblock0
568	  * ....
569	  *   8     0    3968000 sda
570	  *   8     1    3963968 sda1
571	  */
572	while (fgets(line,sizeof(line),fp)) {
573		if (sscanf(line, " %d %d %llu %[^\n ]",
574				&major, &minors, &capacity, part_name) != 4)
575			continue;
576		if (strncmp(part_name, "sd", 2))
577			continue;
578		for (s = part_name; *s; s++)
579			;
580		if (!isdigit(s[-1])) {
581			vendor = get_device_vendor(part_name);
582
583			if (!is_sda(part_name))
584				continue;
585		}
586
587		capacity >>= 10;        /* unit: 1KB .. >> 1   size /512 (long *arg) */
588		if (capacity == 0)
589			continue; /*It indicates that this partition should be an extended partition. */
590
591		if (!is_loop_partition(part_name)){
592			if (is_noshare_partition(part_name))
593				continue;
594		}
595
596		partinfo = malloc(sizeof(struct disk_partition_info));
597		if (partinfo == NULL)
598			continue;
599
600		/* SEE: hotplug2.mount ==> mount /dev/$1 /mnt/$1 */
601		snprintf(mnt_path, sizeof(mnt_path), "/mnt/%s", part_name);
602		/* NO Disk, the mount point directory is NOT removed, this magic value is `0x858458F6` */
603		if (statfs(mnt_path, &statbuf) == 0 && (unsigned int)statbuf.f_type != 0x858458F6 && (unsigned int)statbuf.f_type != TMPFS_MAGIC)
604			partinfo->mounted = 1, have_disk_mouted = 1;
605		else
606			partinfo->mounted = 0;
607		partinfo->afplistupdated = 0;
608		//partinfo->ishfsplus = is_hfsplus_formated(part_name);
609		if ( (is_hfsplus_formated(part_name)) || (is_special_backup_format(part_name)) ){
610			printf("This HDD format can support the feature of Time Machine... ...\n");
611			partinfo->ishfsplus = 1;
612		}else{
613			printf("[Waring]: This HDD format failed to support the Time Machine!!!!!!\n");
614			partinfo->ishfsplus = 0;
615		}
616		if (partinfo->ishfsplus == 1)
617			have_hfsplus_disk_mounted = 1;
618		partinfo->capacity = capacity;
619		if(strncmp(part_name, SATA_DEV_NAME, 3) == 0){
620			partinfo->label = 's' - j;
621			j++;
622		}else if(strncmp(part_name, SD_CARD_DEV_NAME, 3) == 0){
623			partinfo->label = '0' + k;
624			k++;
625		}else{
626			partinfo->label = 'U' - i;
627			i++;
628		}
629		snprintf(partinfo->name, sizeof(partinfo->name),"%s", part_name);
630		if (vendor)
631			strcpy(partinfo->vendor,vendor);
632
633		get_device_id(partinfo);
634		get_disk_volume(partinfo, part_name);
635
636		list_add_tail(&partinfo->list, head);
637
638		USB_DEBUGP("[USB-AFP]: Found partition %s, mounted %s!!!\n", part_name,
639					partinfo->mounted ? "Yes" : "No");
640	}
641
642	fclose(fp);
643	USB_DEBUGP("[USB-AFP]: Total %d partitions are FOUND!\n", i);
644
645	if (have_disk_mouted) {
646		/* reload avahi with afpd and adisk services */
647		if (have_hfsplus_disk_mounted)
648			system("cp /usr/config/avahi/services/afpd.service /etc/avahi/services/ > /dev/null 2>&1");
649		else
650			system("rm /etc/avahi/services/afpd.service > /dev/null 2>&1");
651		system("cp /usr/config/avahi/services/adisk.service /etc/avahi/services/ > /dev/null 2>&1");
652		system("echo \"    <txt-record>sys=waMA=$(/bin/config get wan_factory_mac),adVF=0x1000</txt-record>\" >> /etc/avahi/services/adisk.service");
653	} else {
654		/* reload avahi without afpd and adisk services */
655		system("rm /etc/avahi/services/afpd.service > /dev/null 2>&1");
656		system("rm /etc/avahi/services/adisk.service > /dev/null 2>&1");
657	}
658}
659
660static inline int duplicate_share_name(char *name, struct list_head *head)
661{
662	struct list_head *pos;
663	struct share_info *share;
664
665	list_for_each(pos, head) {
666		share = list_entry(pos, struct share_info, list);
667		if (strcmp(share->name, name) == 0)
668			return 1;
669	}
670
671	return 0;
672}
673
674static inline void add_share_info_list(char *name, struct list_head *head)
675{
676	struct share_info *share;
677
678	share = malloc(sizeof(struct share_info) + strlen(name) + 1);
679	if (share == NULL)
680		return;
681	strcpy(share->name, name);
682	list_add_tail(&share->list, head);
683}
684
685/* encode string to xml-string */
686static char *xml_encode(char *share_name)
687{
688	int i = 0;
689	int output_len = 0;
690	int temp_expansion_len = 0;
691	char *encoded_xml_string = NULL;
692	char temp_expansion[10];
693	char *p1 = NULL;
694
695	p1 = share_name;
696	output_len = (int)strlen(share_name) * 6;
697
698	encoded_xml_string = (char *) malloc(output_len + 1);
699	if (encoded_xml_string == NULL)
700		return NULL;
701
702	while (*p1) {
703		/* alpha-numeric characters don't get encoded */
704		if ((*p1 >= '0' && *p1 <= '9') || (*p1 >= 'A' && *p1 <= 'Z') || (*p1 >= 'a' && *p1 <= 'z')) {
705			encoded_xml_string[i++] = *p1;
706
707		/* spaces, hyphens, periods, underscores and colons don't get encoded */
708		} else if ((*p1 == ' ') || (*p1 == '-') || (*p1 == '.') || (*p1 == '_') || (*p1 == ':')) {
709			encoded_xml_string[i++] = *p1;
710
711		/* ',' char encoded as "\," */
712		} else if (*p1 == ',') {
713			if (i < (output_len - 2)) {
714				strcpy(&encoded_xml_string[i], "\\,");
715				i += 2;
716			}
717
718		/* for simplicity, all other chars represented by their numeric value */
719		} else {
720			snprintf(temp_expansion, 9, "&#%d;", (unsigned char)(*p1));
721			temp_expansion_len = (int)strlen(temp_expansion);
722			if (i < (output_len - temp_expansion_len)) {
723				strcpy(&encoded_xml_string[i], temp_expansion);
724				i += temp_expansion_len;
725			}
726		}
727		p1++;
728	}
729
730	encoded_xml_string[i] = '\0';
731	return encoded_xml_string;
732}
733
734static int update_adisk(int file_fmt, char *share_name)
735{
736	static int cnt = 0;
737	FILE *adisk_conf_fp = NULL;
738	char *encoded_share_name = NULL;
739
740	if((adisk_conf_fp = fopen(AVAHI_SERVICE_ADISK, "a") ) == NULL) {
741		USB_DEBUGP("[USB-AFP]: Unable To Open Adisk Service File.....\n");
742		return -1;
743	}
744
745	fseek(adisk_conf_fp, 0, SEEK_END);
746
747	encoded_share_name = xml_encode(share_name);
748	if (file_fmt == 1) {
749		/* For HFS+ File System that will be shown in the TimeMachine available disk list */
750		fprintf(adisk_conf_fp, "    <txt-record>dk%d=adVF=0x1003,adVN=%s,adVU=</txt-record>\n", cnt++, (encoded_share_name ? encoded_share_name : share_name));
751	} else {
752		/* For other file system that will be not appear in TimeMachine disk list,
753		 * but can be viewed in Finder window */
754		fprintf(adisk_conf_fp, "    <txt-record>dk%d=adVF=0x1002,adVN=%s,adVU=</txt-record>\n", cnt++, (encoded_share_name ? encoded_share_name : share_name));
755	}
756
757	if (encoded_share_name)
758		free(encoded_share_name);
759
760	fclose(adisk_conf_fp);
761	return 0;
762}
763
764static void commit_adisk()
765{
766	char cmd[256];
767
768	memset(cmd,0,256);
769	sprintf(cmd,"echo -e \"  </service>\n</service-group>\" >> %s", AVAHI_SERVICE_ADISK);
770	system(cmd);
771}
772
773static int check_approved_disk(struct disk_partition_info *diskinfo)
774{
775	int i = 0, len = 0;
776	char *p;
777	char approved_info[32], value[512], device_id[128];
778
779        if(config_match("usb_enableUSB", "0"))
780		return 0;
781
782	strncpy(device_id, diskinfo->device_id, 128);
783	for( ; device_id[i] != '*'; i++);
784	device_id[i] = '\0';
785
786	i = 1;
787        for (; ; i++) {
788                sprintf(approved_info, "%s%d", APPROVED_DISK, i);
789                len = sprintf(value, "%s", config_get(approved_info));
790                if (len < 1)
791                        break;
792		p = value + len;
793		for( ; *p != '*'; p--);
794		p++;
795
796		if (strcmp(device_id, p) == 0)
797			return 0;
798	}
799	return 1;
800}
801
802static void load_share_info(FILE *fp, char *diskname)
803{
804	int no_shareinfo = 1; /* If `diskname` is not NULL, check if there is no share info in disk */
805	int num_mounted_disk = 0;
806
807	struct share_info *shareinfo;
808	struct disk_partition_info *diskinfo;
809	struct list_head disk_lists, share_lists, *pos, *nxt;
810
811	INIT_LIST_HEAD(&disk_lists);
812	INIT_LIST_HEAD(&share_lists);
813
814	scan_disk_entries(&disk_lists);
815
816	USB_DEBUGP("[USB-AFP]: Loading USB share information ......diskname: %s\n", diskname);
817	list_for_each(pos, &disk_lists) {
818		int j;
819		char name[64], device_id[128], oneline[1024];
820		char *val, *volumeName, *deviceName, *serial_num, *partition_id;
821		char fullpath[USB_PATH_SIZE],dupshare[USB_PATH_SIZE];
822		char *sep, *t_share_name, *folderName, *readAccess, *writeAccess;
823		char  share_name[128];
824
825		diskinfo = list_entry(pos, struct disk_partition_info, list);
826		if (!diskinfo->mounted)
827			continue;
828		if (check_approved_disk(diskinfo)){
829			no_shareinfo = 0;
830			continue;
831		}
832
833		sep = "*\n";
834		for (j=0; ;j++) {
835			sprintf(name, SHARE_FILE_INFO"%d",j);
836			val = config_get(name);
837			if (*val == '\0')
838				break;
839
840			strcpy(oneline, val);
841
842			t_share_name = strtok(oneline, sep); /* share name */
843			folderName = strtok(NULL, sep);	/* folder name */
844			readAccess = strtok(NULL, sep);	/* readAccess*/
845			writeAccess = strtok(NULL, sep);	/* writeAccess */
846			volumeName = strtok(NULL, sep);   /* volumeName*/
847			deviceName = strtok(NULL, sep);    /* deviceName */
848			serial_num = strtok(NULL, sep);	/* serialNum */
849			partition_id = strtok(NULL, sep);	/* partitionNum */
850
851			memset(share_name, 0, 128);
852			sprintf(share_name, "%s", t_share_name);
853
854			if (share_name == NULL || folderName == NULL || readAccess == NULL ||writeAccess == NULL ||
855				volumeName == NULL || deviceName == NULL )
856				continue;
857
858			snprintf(device_id, sizeof(device_id), "%s*%s", serial_num, partition_id);
859			if(strcmp(device_id,diskinfo->device_id) || strcmp(volumeName,diskinfo->vol_name) || strcmp(deviceName,diskinfo->vendor))
860				continue;
861
862			if (duplicate_share_name(share_name, &share_lists)) {
863				// Fixme: if volume name also different then dupplicate.
864				snprintf(dupshare, sizeof(dupshare), "%s(%c)", share_name, diskinfo->label);
865				memset(share_name, 0, 128);
866				sprintf(share_name,"%s", dupshare);
867			}
868
869			add_share_info_list(share_name, &share_lists);
870
871			snprintf(fullpath, sizeof(fullpath), "/mnt/%s%s", diskinfo->name, folderName);
872
873			readAccess = user_name(readAccess);
874			writeAccess = user_name(writeAccess);
875
876			USB_DEBUGP("[USB-AFP]: AFPInfo %s Folder:%s Reader:%s Writer: %s\n", share_name, folderName, readAccess, writeAccess);
877
878			if (diskinfo->ishfsplus) {
879				add_afpd_share_info(fp, share_name, readAccess, writeAccess, fullpath);
880				update_adisk(1,share_name);
881			} else {
882				update_adisk(0,share_name);
883			}
884
885			if (itunes_db_folder[0] == 0)
886				sprintf(itunes_db_folder, "/tmp/mnt/%s/.itunes", diskinfo->name);
887			if (strcmp(readAccess, USER_GUEST) == 0) {
888				int len = strlen(itunes_share_floders);
889				if (len == 0)
890					sprintf(itunes_share_floders, "\"%s\"", fullpath);
891				else if(len + strlen(fullpath) + 5 < ITUNES_SHARE_FOLDER_SIZE)
892					sprintf(itunes_share_floders + len, ", \"%s\"", fullpath);
893				else
894					printf("Warning: %s is not added in itunes_share_floders\n", fullpath);
895			}
896
897			diskinfo->afplistupdated = 1;
898			num_mounted_disk++;
899			USB_DEBUGP("\nIn First Step: valume: %s afplist: %d", diskinfo->vol_name, diskinfo->afplistupdated);
900
901			if (diskname != NULL && strncmp(diskinfo->name, diskname, 3) == 0)
902				no_shareinfo = 0;
903		}
904	}
905
906	list_for_each_safe(pos, nxt, &disk_lists) {
907		diskinfo = list_entry(pos, struct disk_partition_info, list);
908		list_del(pos);
909		free(diskinfo);
910	}
911
912	list_for_each_safe(pos, nxt, &share_lists) {
913		shareinfo = list_entry(pos, struct share_info, list);
914		list_del(pos);
915		free(shareinfo);
916	}
917
918	if (num_mounted_disk > 0) {
919		commit_adisk();
920	}
921}
922
923void cleanup(int signal)
924{
925	printf("Try to recover from endless waiting.\n");
926	reload_services();
927	unlink(TMP_AFP_LOCK);
928	exit(1);
929}
930
931int check_afp_locked(void)
932{
933	return (!access(TMP_AFP_LOCK, F_OK));
934}
935
936void check_sata_dev(void)
937{
938	strcpy(SATA_DEV_NAME, config_get("sata_diskname"));
939	strcpy(SATA_DEV_SERIAL_NO, config_get("sata_serial_no"));
940}
941
942void check_sd_card_dev(void)
943{
944	strcpy(SD_CARD_DEV_NAME, config_get("sd_card_diskname"));
945}
946
947
948int main(int argc, char**argv)
949{
950	FILE *fp, *filp;
951	char *diskname = NULL;
952	char *device_name;
953	struct timeval currenttime, newtime;
954
955	signal(SIGINT, cleanup);
956	signal(SIGTERM, cleanup);
957
958	gettimeofday(&currenttime, NULL);
959
960	while (check_afp_locked()) {
961		gettimeofday(&newtime, NULL);
962		/* the longest waiting time is 30s, avoid endless waiting */
963		if ((newtime.tv_sec - currenttime.tv_sec) > 30)
964			cleanup(0);
965		sleep(1);
966	}
967
968	/* create lock file */
969	filp = fopen(TMP_AFP_LOCK, "w+");
970	if (filp)
971		fclose(filp);
972	else {
973		perror("error when creating afp_lock file!\n");
974		return 1;
975	}
976
977	check_sata_dev();
978	check_sd_card_dev();
979
980	fp = fopen(USB_APPLE_VOLUMES_DEFAULT_CONF, "w");
981	if (fp == NULL)
982		goto unlock;
983
984	if (argc == 2 && strlen(argv[1]) == 3 && strncmp(argv[1], "sd", 2) == 0)
985		diskname = argv[1];	/* sd[a-z] */
986
987	load_share_info(fp, diskname);
988
989	fclose(fp);
990
991	reload_services();
992
993	system("/etc/init.d/forked-daapd stop");
994	if (config_match("endis_itunes", "1") && itunes_share_floders[0] != 0 && (fp = fopen("/etc/forked-daapd.conf", "w")) != NULL) {
995		device_name  = config_get("upnp_serverName");
996		if (*device_name != '\0')
997			fprintf(fp, daapd_conf, itunes_db_folder, itunes_db_folder, config_get("upnp_serverName"), itunes_share_floders);
998		else
999			fprintf(fp, daapd_conf, itunes_db_folder, itunes_db_folder, config_get("Device_name"), itunes_share_floders);
1000		fclose(fp);
1001		char filename[256];
1002
1003		if (access(itunes_db_folder, 0))
1004			mkdir(itunes_db_folder, 0777);
1005		else {
1006			sprintf(filename, "%s/forked-daapd.log", itunes_db_folder);
1007			unlink(filename);
1008			sprintf(filename, "%s/forked-daapd.db", itunes_db_folder);
1009			unlink(filename);
1010		}
1011		system("/etc/init.d/forked-daapd start");
1012	}
1013unlock:
1014	unlink(TMP_AFP_LOCK);
1015	return 0;
1016}
1017