1 /*
2 sitecopy, for managing remote web sites.
3 Copyright (C) 1998-2006, Joe Orton <joe@manyfish.co.uk>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <config.h>
22
23 #include <sys/types.h>
24
25 #include <sys/stat.h>
26
27 #include <ctype.h>
28 #include <errno.h>
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
32 #ifdef HAVE_STRINGS_H
33 #include <strings.h>
34 #endif
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif /* HAVE_STDLIB_H */
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif /* HAVE_UNISTD_H */
41
42 #include <ne_string.h>
43 #include <ne_alloc.h>
44
45 #include "common.h"
46 #include "netrc.h"
47 #include "rcfile.h"
48 #include "sites.h"
49
50 /** Global variables **/
51 char *copypath;
52 char *rcfile;
53 char *netrcfile;
54 char *home;
55 int havenetrc;
56
57 /* These are used for reporting errors back to the calling procedures. */
58 int rcfile_linenum;
59 char *rcfile_err;
60
61 /** Not quite so global variables **/
62
63 /* These are appended to $HOME */
64 #define RCNAME "/.sitecopyrc"
65 #define COPYNAME "/.sitecopy/"
66 #define NETRCNAME "/.netrc"
67
68 /* Stores the list of entries in the ~/.netrc */
69 netrc_entry *netrc_list;
70
71 const char *rc_get_netrc_password(const char *server, const char *username);
72
73 /* The driver definitions */
74 #ifdef USE_FTP
75 extern const struct proto_driver ftp_driver;
76 #endif /* USE_FTP */
77 #ifdef USE_DAV
78 extern const struct proto_driver dav_driver;
79 #endif /* USE_DAV */
80 #ifdef USE_RSH
81 extern const struct proto_driver rsh_driver;
82 #endif /* USE_RSH */
83 #ifdef USE_SFTP
84 extern const struct proto_driver sftp_driver;
85 #endif /* USE_SFTP */
86
87 /* rcfile_read will read the rcfile and fill given sites list.
88 * This returns 0 on success, RC_OPENFILE if the rcfile could not
89 * be read, or RC_CORRUPT if the rcfile was corrupt.
90 * If it is corrupt, rcfile_linenum and rcfile_line are set to the
91 * the corrupt line.
92 */
93 #define LINESIZE 128
rcfile_read(struct site ** sites)94 int rcfile_read(struct site **sites)
95 {
96 FILE *fp;
97 int state, last_state=8, ret=0;
98 int alpha, hash;
99 char buf[LINESIZE];
100 char *ch;
101 char *ptr, key[LINESIZE], val[LINESIZE], val2[LINESIZE];
102 /* Holders for the site info, and default site settings */
103 struct site *this_site, *last_site, default_site = {0};
104
105 if ((fp = fopen(rcfile, "r")) == NULL) {
106 rcfile_err = strerror(errno);
107 return RC_OPENFILE;
108 }
109
110 default_site.perms = sitep_ignore;
111 default_site.symlinks = sitesym_follow;
112 default_site.protocol = siteproto_ftp;
113 default_site.proto_string = ne_strdup("ftp");
114
115 default_site.ftp_pasv_mode = true;
116 default_site.ftp_use_cwd = false;
117
118 last_site = this_site = NULL;
119 rcfile_linenum = 0;
120 rcfile_err = NULL;
121
122 while ((ret==0) && (fgets(buf, sizeof(buf), fp) != NULL)) {
123 rcfile_linenum++;
124 /* Put the line without the LF into the error buffer */
125 if (rcfile_err != NULL) free(rcfile_err);
126 rcfile_err = ne_strdup(buf);
127 ptr = strchr(rcfile_err, '\n');
128 if (ptr != NULL) *ptr = '\0';
129 state = 0;
130 ptr = key;
131 memset(key, 0, LINESIZE);
132 memset(val, 0, LINESIZE);
133 memset(val2, 0, LINESIZE);
134 for (ch=buf; *ch!='\0'; ch++) {
135 alpha = !isspace((unsigned)*ch); /* well, alphaish */
136 hash = (*ch == '#');
137 switch (state) {
138 case 0: /* whitespace at beginning of line */
139 if (hash) {
140 state = 8;
141 } else if (alpha) {
142 *(ptr++) = *ch;
143 state = 1;
144 }
145 break;
146 case 1: /* key */
147 if (hash) {
148 state = 8;
149 } else if (!alpha) {
150 ptr = val;
151 state = 2;
152 } else {
153 *(ptr++) = *ch;
154 }
155 break;
156 case 2: /* whitespace after key */
157 if (hash) {
158 state = 8;
159 } else if (*ch == '"') {
160 state = 4; /* begin quoted value */
161 } else if (alpha) {
162 *(ptr++) = *ch;
163 state = 3;
164 }
165 break;
166 case 3: /* unquoted value 1 */
167 if (hash) {
168 state = 8;
169 } else if (!alpha) {
170 ptr = val2;
171 state = 5;
172 } else {
173 *(ptr++) = *ch;
174 }
175 break;
176 case 4: /* quoted value 1 */
177 if (*ch == '"') {
178 ptr = val2;
179 state = 5;
180 } else if (*ch == '\\') {
181 last_state = 4;
182 state = 9;
183 } else {
184 *(ptr++) = *ch;
185 }
186 break;
187 case 5: /* whitespace after value 1 */
188 if (hash) {
189 state = 8;
190 } else if (*ch == '"') {
191 state = 6; /* begin quoted value 2 */
192 } else if (alpha) {
193 *(ptr++) = *ch;
194 state = 7; /* begin unquoted value 2 */
195 }
196 break;
197 case 6: /* quoted value 2 */
198 if (*ch == '"') {
199 state = 8;
200 } else if (*ch == '\\') {
201 last_state = 4;
202 state = 9;
203 } else {
204 *(ptr++) = *ch;
205 }
206 break;
207 case 7: /* unquoted value 2 */
208 if (hash) {
209 state = 8;
210 } else if (!alpha) {
211 state = 8;
212 } else {
213 *(ptr++) = *ch;
214 }
215 break;
216 case 8: /* ignore till end of line */
217 break;
218 case 9: /* a literal (\-slashed) in a value */
219 *(ptr++) = *ch;
220 state = last_state;
221 break;
222 }
223 }
224
225 NE_DEBUG(DEBUG_RCFILE, "Key [%s] Value: [%s] Value2: [%s]\n", key, val, val2);
226
227 if (strlen(key) == 0) {
228 continue;
229 }
230 if (strlen(val) == 0) {
231 /* A key with no value. */
232 if (this_site == NULL) {
233 if (strcmp(key, "default") == 0) {
234 /* Setting up the default site */
235 NE_DEBUG(DEBUG_RCFILE, "Default site entry:\n");
236 this_site = &default_site;
237 } else {
238 /* Need to be in a site! */
239 ret = RC_CORRUPT;
240 }
241 } else if (strcmp(key, "nodelete") == 0) {
242 this_site->nodelete = true;
243 } else if (strcmp(key, "checkmoved") == 0) {
244 this_site->checkmoved = true;
245 } else if (strcmp(key, "nooverwrite") == 0) {
246 this_site->nooverwrite = true;
247 } else if (strcmp(key, "lowercase") == 0) {
248 this_site->lowercase = true;
249 } else if (strcmp(key, "safe") == 0) {
250 this_site->safemode = true;
251 } else if (strcmp(key, "tempupload") == 0) {
252 this_site->tempupload = true;
253 } else {
254 ret = RC_CORRUPT;
255 }
256 } else if (strlen(val2) == 0) {
257 /* A key with a single value. */
258 if (strcmp(key, "site") == 0) {
259 /* Beginning of a new Site */
260 if (this_site != &default_site)
261 last_site = this_site;
262 /* Allocate new item */
263 this_site = ne_malloc(sizeof(struct site));
264 /* Copy over the defaults */
265 memcpy(this_site, &default_site, sizeof(struct site));
266 /* Deep-copy the string lists */
267 this_site->excludes = fnlist_deep_copy(default_site.excludes);
268 this_site->ignores = fnlist_deep_copy(default_site.ignores);
269 this_site->asciis = fnlist_deep_copy(default_site.asciis);
270 this_site->prev = last_site;
271 if (last_site != NULL) { /* next site */
272 last_site->next = this_site;
273 } else { /* First site */
274 *sites = this_site;
275 }
276 this_site->name = ne_strdup(val);
277 this_site->files = NULL;
278 this_site->proto_string = ne_strdup(default_site.proto_string);
279 /* Now work out the info filename */
280 this_site->infofile = ne_concat(copypath, val, NULL);
281 this_site->certfile = ne_concat(copypath, val, ".crt", NULL);
282 } else if (this_site == NULL) {
283 ret = RC_CORRUPT;
284 } else if (strcmp(key, "username") == 0) {
285 /* username */
286 this_site->server.username = ne_strdup(val);
287 } else if (strcmp(key, "server") == 0) {
288 this_site->server.hostname = ne_strdup(val);
289 } else if (strcmp(key, "port") == 0) {
290 this_site->server.port = atoi(val);
291 } else if (strcmp(key, "proxy-server") == 0) {
292 this_site->proxy.hostname = ne_strdup(val);
293 } else if (strcmp(key, "proxy-port") == 0) {
294 this_site->proxy.port = atoi(val);
295 } else if (strcmp(key, "proxy-password") == 0) {
296 this_site->proxy.password = ne_strdup(val);
297 } else if (strcmp(key, "proxy-username") == 0) {
298 this_site->proxy.username = ne_strdup(val);
299 } else if (strcmp(key, "password") == 0) {
300 this_site->server.password = ne_strdup(val);
301 } else if (strcmp(key, "url") == 0) {
302 this_site->url = ne_strdup(val);
303 } else if (strcmp(key, "remote") == 0) {
304 /* Relative filenames must start with "~/" */
305 if (val[0] == '~') {
306 if (val[1] == '/') {
307 this_site->remote_isrel = true;
308 } else {
309 ret = RC_CORRUPT;
310 }
311 } else {
312 /* Dirname doesn't begin with "~/" */
313 this_site->remote_isrel = false;
314 }
315 if (val[strlen(val)-1] != '/')
316 strcat(val, "/");
317 this_site->remote_root_user = ne_strdup(val);
318 } else if (strcmp(key, "local") == 0) {
319 /* Relative filenames must start with "~/" */
320 if (val[0] == '~') {
321 if (val[1] == '/') {
322 this_site->local_isrel = true;
323 } else {
324 ret = RC_CORRUPT;
325 }
326 } else {
327 /* Dirname doesn't begin with a "~/" */
328 this_site->local_isrel = false;
329 }
330 if (val[strlen(val)-1] != '/')
331 strcat(val, "/");
332 this_site->local_root_user = ne_strdup(val);
333 } else if (strcmp(key, "permissions") == 0) {
334 if (strcmp(val, "ignore") == 0) {
335 this_site->perms = sitep_ignore;
336 this_site->dirperms = 0;
337 } else if (strcmp(val, "exec") == 0) {
338 this_site->perms = sitep_exec;
339 } else if (strcmp(val, "all") == 0) {
340 this_site->perms = sitep_all;
341 } else if (strcmp(val, "dir") == 0) {
342 this_site->dirperms = 1;
343 } else {
344 ret = RC_CORRUPT;
345 }
346 } else if (strcmp(key, "symlinks") == 0) {
347 if (strcmp(val, "follow") == 0) {
348 this_site->symlinks = sitesym_follow;
349 } else if (strcmp(val, "maintain") == 0) {
350 this_site->symlinks = sitesym_maintain;
351 } else if (strcmp(val, "ignore") == 0) {
352 this_site->symlinks = sitesym_ignore;
353 } else {
354 ret = RC_CORRUPT;
355 }
356 } else if (strcmp(key, "exclude") == 0) {
357 struct fnlist *f = fnlist_prepend(&this_site->excludes);
358 if (val[0] == '/') {
359 f->pattern = ne_strdup(val+1);
360 f->haspath = true;
361 } else {
362 f->pattern = ne_strdup(val);
363 f->haspath = false;
364 }
365 } else if (strcmp(key, "ignore") == 0) {
366 struct fnlist *f = fnlist_prepend(&this_site->ignores);
367 if (val[0] == '/') {
368 f->pattern = ne_strdup(val+1);
369 f->haspath = true;
370 } else {
371 f->pattern = ne_strdup(val);
372 f->haspath = false;
373 }
374 } else if (strcmp(key, "ascii") == 0) {
375 struct fnlist *f = fnlist_prepend(&this_site->asciis);
376 if (val[0] == '/') {
377 f->pattern = ne_strdup(val+1);
378 f->haspath = true;
379 } else {
380 f->pattern = ne_strdup(val);
381 f->haspath = false;
382 }
383 } else if (strcmp(key, "protocol") == 0) {
384 if (strcasecmp(val, "ftp") == 0) {
385 this_site->protocol = siteproto_ftp;
386 } else if (strcasecmp(val, "http") == 0 ||
387 strcasecmp(val, "dav") == 0 ||
388 strcasecmp(val, "webdav") == 0) {
389 this_site->protocol = siteproto_dav;
390 } else if (strcasecmp(val, "rsh") == 0) {
391 this_site->protocol = siteproto_rsh;
392 } else if (strcasecmp(val, "ssh") == 0) {
393 this_site->protocol = siteproto_rsh;
394 if (this_site->rsh_cmd == NULL)
395 this_site->rsh_cmd = ne_strdup("ssh");
396 if (this_site->rcp_cmd == NULL)
397 this_site->rcp_cmd = ne_strdup("scp");
398 } else if (strcasecmp(val, "sftp") == 0) {
399 this_site->protocol = siteproto_sftp;
400 } else {
401 this_site->protocol = siteproto_unknown;
402 }
403 free(this_site->proto_string);
404 this_site->proto_string = ne_strdup(val);
405 } else if (strcmp(key, "ftp") == 0) {
406 if (strcmp(val, "nopasv") == 0) {
407 this_site->ftp_pasv_mode = false;
408 } else if (strcmp(val, "showquit") == 0) {
409 this_site->ftp_echo_quit = true;
410 } else if (strcmp(val, "usecwd") == 0) {
411 this_site->ftp_use_cwd = true;
412 } else if (strcmp(val, "nousecwd") == 0) {
413 this_site->ftp_use_cwd = false;
414 } else {
415 ret = RC_CORRUPT;
416 }
417 } else if (strcmp(key, "http") == 0) {
418 if (strcmp(val, "expect") == 0) {
419 this_site->http_use_expect = true;
420 } else if (strcmp(val, "limit") == 0) {
421 this_site->http_limit = true;
422 } else if (strcmp(val, "secure") == 0) {
423 this_site->http_secure = true;
424 } else if (strcmp(val, "tolerant") == 0) {
425 this_site->http_tolerant = true;
426 } else {
427 ret = RC_CORRUPT;
428 }
429 } else if (strcmp(key, "client-cert") == 0) {
430 this_site->client_cert = ne_strdup(val);
431 } else if (strcmp(key, "rsh") == 0) {
432 this_site->rsh_cmd = ne_strdup(val);
433 } else if (strcmp(key, "rcp") == 0) {
434 this_site->rcp_cmd = ne_strdup(val);
435 } else if (strcmp(key, "state") == 0) {
436 if (strcmp(val, "checksum") == 0) {
437 this_site->state_method = state_checksum;
438 } else if (strcmp(val, "timesize") == 0) {
439 this_site->state_method = state_timesize;
440 } else {
441 ret = RC_CORRUPT;
442 }
443 } else if (strcmp(key, "checkmoved") == 0) {
444 if (strcmp(val, "renames") == 0) {
445 this_site->checkrenames = true;
446 this_site->checkmoved = true;
447 } else {
448 ret = RC_CORRUPT;
449 }
450 } else if (strcmp(key, "charset") == 0) {
451 NE_DEBUG(DEBUG_RCFILE, "Ignored key %s\n", key);
452 } else {
453 /* Unknown key! */
454 ret = RC_CORRUPT;
455 }
456 } else {
457 {
458 ret = RC_CORRUPT;
459 }
460 }
461 }
462
463 fclose(fp);
464 return ret;
465 }
466 #undef LINESIZE
467
rc_get_netrc_password(const char * server,const char * username)468 const char *rc_get_netrc_password(const char *server, const char *username) {
469 netrc_entry *found;
470 found = search_netrc(netrc_list, server);
471 if (found == NULL) {
472 return NULL;
473 }
474 if (strcmp(found->account, username) == 0) {
475 return found->password;
476 } else {
477 return NULL;
478 }
479 }
480
481 /* Returns zero if site is properly defined, else non-zero */
rcfile_verify(struct site * any_site)482 int rcfile_verify(struct site *any_site)
483 {
484 struct stat localst;
485 char *temp;
486 int ret;
487
488 /* Protocol-specific checks first, since if a new protocol driver is used,
489 * any of the other checks may be irrelevant. */
490 switch (any_site->protocol) {
491 case siteproto_ftp:
492 #ifdef USE_FTP
493 any_site->driver = &ftp_driver;
494 /* FTP checks */
495 if (any_site->symlinks == sitesym_maintain) {
496 return SITE_NOMAINTAIN;
497 }
498 break;
499 #else /* !USE_FTP */
500 return SITE_UNSUPPORTED;
501 #endif /* USE_FTP */
502 case siteproto_dav:
503 #ifdef USE_DAV
504 any_site->driver = &dav_driver;
505 /* HTTP checks */
506 if (any_site->remote_isrel) {
507 return SITE_NOREMOTEREL;
508 }
509 if (any_site->perms == sitep_all || any_site->dirperms) {
510 return SITE_NOPERMS;
511 }
512 if (any_site->symlinks == sitesym_maintain) {
513 return SITE_NOMAINTAIN;
514 }
515 break;
516 #else /* !USE_DAV */
517 return SITE_UNSUPPORTED;
518 #endif /* USE_DAV */
519 case siteproto_rsh:
520 #ifdef USE_RSH
521 any_site->driver = &rsh_driver;
522 /* FIXME: rsh checks? */
523 break;
524 #else /* !USE_RSH */
525 return SITE_UNSUPPORTED;
526 #endif /* USE_RSH */
527 case siteproto_sftp:
528 #ifdef USE_SFTP
529 any_site->driver = &sftp_driver;
530 /* FIXME: sftp checks? */
531 break;
532 #else /* !USE_SFTP */
533 return SITE_UNSUPPORTED;
534 #endif /* USE_SFTP */
535 case siteproto_unknown:
536 return SITE_UNSUPPORTED;
537 }
538
539 /* Valid options check */
540 if (any_site->checkrenames && (any_site->state_method != state_checksum)) {
541 return SITE_NORENAMES;
542 }
543
544 /* Check they specified everything in the rcfile */
545 if (any_site->server.hostname == NULL) {
546 return SITE_NOSERVER;
547 }
548
549 if (any_site->server.username != NULL && any_site->server.password == NULL) {
550 if (havenetrc) {
551 const char *pass;
552 NE_DEBUG(DEBUG_RCFILE, "Checking netrc for password for %s@%s...",
553 any_site->server.username, any_site->server.hostname);
554 pass = rc_get_netrc_password(any_site->server.hostname,
555 any_site->server.username);
556 if (pass != NULL) {
557 NE_DEBUG(DEBUG_RCFILE, "found!\n");
558 any_site->server.password = (char *) pass;
559 } else {
560 NE_DEBUG(DEBUG_RCFILE, "none found.\n");
561 }
562 }
563 }
564 /* TODO: lookup proxy username/password in netrc too */
565
566 if (any_site->remote_root_user == NULL) {
567 return SITE_NOREMOTEDIR;
568 } else if (any_site->local_root_user == NULL) {
569 return SITE_NOLOCALDIR;
570 }
571
572 /* Need a home directory if we're using relative local root */
573 if (home == NULL && any_site->local_root)
574 return SITE_NOLOCALREL;
575
576 /* Can't use safe mode and nooverwrite mode */
577 if (any_site->safemode && any_site->nooverwrite)
578 return SITE_NOSAFEOVER;
579
580 if (any_site->safemode && any_site->tempupload)
581 return SITE_NOSAFETEMPUP;
582
583 if (any_site->remote_isrel) {
584 any_site->remote_root = ne_strdup(any_site->remote_root_user + 2);
585 } else {
586 any_site->remote_root = ne_strdup(any_site->remote_root_user);
587 }
588 if (any_site->local_isrel) {
589 /* We skip the first char ('~') of l_r_u */
590 any_site->local_root = ne_concat(home, any_site->local_root_user + 1,
591 NULL);
592 } else {
593 any_site->local_root = any_site->local_root_user;
594 }
595
596 /* Now check the local directory actually exists.
597 * To do this, stat `/the/local/root/.', which will fail if the
598 * can't read the directory or if it's a file not a directory */
599 temp = ne_concat(any_site->local_root, ".", NULL);
600 ret = stat(temp, &localst);
601 free(temp);
602 if (ret != 0) {
603 return SITE_ACCESSLOCALDIR;
604 }
605
606 if (any_site->client_cert && strncmp(any_site->client_cert, "~/", 2) == 0) {
607 temp = ne_concat(home, any_site->client_cert + 1, NULL);
608 ne_free(any_site->client_cert);
609 any_site->client_cert = temp;
610 }
611
612 /* Assign default ports if they didn't bother to */
613 if (any_site->server.port == 0) {
614 NE_DEBUG(DEBUG_RCFILE, "Lookup up default port:\n");
615 any_site->server.port = (*any_site->driver->get_server_port)(any_site);
616 NE_DEBUG(DEBUG_RCFILE, "Using port: %d\n", any_site->server.port);
617 }
618
619 if (any_site->proxy.port == 0) {
620 NE_DEBUG(DEBUG_RCFILE, "Lookup default proxy port...\n");
621 any_site->proxy.port = (*any_site->driver->get_proxy_port)(any_site);
622 NE_DEBUG(DEBUG_RCFILE, "Using port %d\n", any_site->proxy.port);
623 }
624
625 /* TODO: ditto for proxy server */
626 return 0;
627 }
628
init_netrc()629 int init_netrc() {
630 if (!havenetrc) return 0;
631 netrc_list = parse_netrc(netrcfile);
632 if (netrc_list == NULL) {
633 /* Couldn't parse it */
634 return 1;
635 } else {
636 /* Could parse it */
637 return 0;
638 }
639 }
640
641 /* Checks the perms of the rcfile and site storage directory. */
init_paths()642 int init_paths()
643 {
644 struct stat st;
645 if (stat(rcfile, &st) < 0) {
646 NE_DEBUG(DEBUG_RCFILE, "stat failed on %s: %s\n",
647 rcfile, strerror(errno));
648 return RC_OPENFILE;
649 }
650 #if !defined (__EMX__) && !defined(__CYGWIN__)
651 if (!S_ISREG(st.st_mode)) {
652 return RC_OPENFILE;
653 }
654 if ((st.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) > 0) {
655 return RC_PERMS;
656 }
657 #endif
658 if ((netrcfile == 0) || (stat(netrcfile, &st) < 0)) {
659 havenetrc = false;
660 #if !defined (__EMX__) && !defined(__CYGWIN__)
661 } else if ((st.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) > 0) {
662 return RC_NETRCPERMS;
663 #endif
664 } else {
665 havenetrc = true;
666 }
667 if (stat(copypath, &st) < 0) {
668 NE_DEBUG(DEBUG_RCFILE, "stat failed on %s: %s\n",
669 copypath, strerror(errno));
670 return RC_DIROPEN;
671 }
672 #if !defined (__EMX__) && !defined(__CYGWIN__)
673 if (st.st_mode & (S_IRWXG | S_IRWXO)) {
674 return RC_DIRPERMS;
675 }
676 #endif
677 return 0;
678 }
679
init_env()680 int init_env() {
681 /* Assign default filenames if they didn't give us any */
682 home = getenv("HOME");
683 if (home == NULL) {
684 if ((rcfile == NULL) || (copypath == NULL)) {
685 /* We need a $HOME or both rcfile and info dir path */
686 return 1;
687 } else {
688 /* No $HOME, but we've got the rcfile and info dir path */
689 return 0;
690 }
691 }
692 if (rcfile == NULL) {
693 rcfile = ne_concat(home, RCNAME, NULL);
694 }
695 if (copypath == NULL) {
696 copypath = ne_concat(home, COPYNAME, NULL);
697 }
698 netrcfile = ne_concat(home, NETRCNAME, NULL);
699 return 0;
700 }
701
702 /* rcfile_write() by Lee Mallabone, cleaned by JO.
703 * Write the contents of list_of_sites to the specified 'filename'
704 * in the standard sitecopy rc format.
705 *
706 * Any data already in 'filename' is over-written.
707 */
rcfile_write(char * filename,struct site * list_of_sites)708 int rcfile_write (char *filename, struct site *list_of_sites)
709 {
710 struct site *current;
711 struct fnlist *item;
712 FILE *fp;
713
714 fp = fopen (filename, "w");
715 if (fp == NULL) {
716 printf ("There was a problem writing to the sitecopy configuration file.\n\nCheck permissions on %s.", filename);
717 return RC_OPENFILE;
718 }
719
720 /* Set rcfile permissions properly */
721 #if !defined (__EMX__) && !defined(__CYGWIN__)
722 if (fchmod (fileno(fp), 00600) == -1) {
723 return RC_PERMS;
724 }
725 #endif
726
727 for (current=list_of_sites; current!=NULL; current=current->next) {
728 /* Okay so this maybe isn't the most intuitive thing to look at.
729 * With any luck though, the rcfile's it produces will be. :) */
730 if (fprintf (fp, "site %s\n", current->name) == -1) {
731 return RC_CORRUPT;
732 }
733 if (fprintf (fp, " server %s\n", current->server.hostname) == -1) {
734 return RC_CORRUPT;
735 }
736
737 if ((current->server.username != NULL) &&
738 (strlen(current->server.username) > 0))
739 if (fprintf(fp, " username %s\n",
740 current->server.username) == -1) {
741 return RC_CORRUPT;
742 }
743
744 if ((current->server.password != NULL)
745 && (strlen(current->server.password) > 0))
746 if (fprintf(fp, " password %s\n",
747 current->server.password) == -1) {
748 return RC_CORRUPT;
749 }
750
751 if (fprintf(fp, " remote %s\n local %s\n",
752 current->remote_root_user,
753 current->local_root_user) == -1) {
754 return RC_CORRUPT;
755 }
756
757 if (fprintf (fp, " protocol %s\n", current->proto_string) == -1) {
758 return RC_CORRUPT;
759 }
760
761 /* Makes sense to have protocol (ish) options after we specify
762 * the protocol. Warning, if the http declarations in site_t
763 * are ever surrounded by an ifdef USE_DAV, then this will need
764 * to be changed. */
765
766 /* Write out the boolean fields */
767
768 #define RCWRITEBOOL(field,name) \
769 if ((field) && (fprintf(fp, " %s\n", name) == -1)) return RC_CORRUPT;
770
771 RCWRITEBOOL(current->nodelete, "nodelete");
772 if (current->checkmoved) {
773 if (current->checkrenames) {
774 if (fprintf(fp, " checkmoved renames\n") == -1)
775 return RC_CORRUPT;
776 } else {
777 if (fprintf(fp, " checkmoved\n") == -1)
778 return RC_CORRUPT;
779 }
780 }
781
782 RCWRITEBOOL(current->nooverwrite, "nooverwrite");
783 RCWRITEBOOL(current->safemode, "safe");
784 RCWRITEBOOL(current->lowercase, "lowercase");
785 RCWRITEBOOL(current->tempupload, "tempupload");
786
787 RCWRITEBOOL(!current->ftp_pasv_mode, "ftp nopasv");
788 RCWRITEBOOL(current->ftp_echo_quit, "ftp showquit");
789 RCWRITEBOOL(current->ftp_use_cwd, "ftp usecwd");
790 RCWRITEBOOL(current->http_limit, "http limit");
791 RCWRITEBOOL(current->http_use_expect, "http expect");
792
793 #undef RCWRITEBOOL
794
795 if (current->server.port > 0) { /* Sanity check */
796 if (fprintf (fp, " port %d\n", current->server.port) == -1) {
797 return RC_CORRUPT;
798 }
799 }
800
801 /* Add the site's URL if one has been supplied. */
802 if (current->url) {
803 if (fprintf (fp, " url %s\n", current->url) == -1) {
804 return RC_CORRUPT;
805 }
806 }
807
808 /* State method */
809 switch (current->state_method) {
810 case (state_timesize):
811 if (fprintf (fp, " state timesize\n") == -1) {
812 return RC_CORRUPT;
813 }
814 break;
815 case (state_checksum):
816 if (fprintf (fp, " state checksum\n") == -1) {
817 return RC_CORRUPT;
818 }
819 break;
820 }
821
822 /* Permissions now */
823 switch (current->perms) {
824 case (sitep_ignore):
825 if (fprintf (fp, " permissions ignore\n") == -1) {
826 return RC_CORRUPT;
827 }
828 break;
829 case (sitep_exec):
830 if (fprintf (fp, " permissions exec\n") == -1) {
831 return RC_CORRUPT;
832 }
833 break;
834 case (sitep_all):
835 if (fprintf (fp, " permissions all\n") == -1) {
836 return RC_CORRUPT;
837 }
838 break;
839 }
840
841 if (current->dirperms) {
842 if (fprintf(fp, " permissions dir\n") == -1) {
843 return RC_CORRUPT;
844 }
845 }
846
847 /* Sym link mode */
848 switch (current->symlinks) {
849 case (sitesym_ignore):
850 if (fprintf (fp, " symlinks ignore\n") == -1) {
851 return RC_CORRUPT;
852 }
853 break;
854 case (sitesym_follow):
855 if (fprintf (fp, " symlinks follow\n") == -1) {
856 return RC_CORRUPT;
857 }
858 break;
859 case (sitesym_maintain):
860 if (fprintf (fp, " symlinks maintain\n") == -1) {
861 return RC_CORRUPT;
862 }
863 break;
864 }
865
866 #define DUMP_FNLIST(list, name) \
867 do {\
868 for (item = list; item != NULL; item = item->next) \
869 if (fprintf(fp, " " name " \"%s%s\"\n", item->haspath?"/":"", \
870 item->pattern) == -1) \
871 return RC_CORRUPT; \
872 } while(0)
873
874 DUMP_FNLIST(current->excludes, "exclude");
875 DUMP_FNLIST(current->asciis, "ascii");
876 DUMP_FNLIST(current->ignores, "ignore");
877
878 #undef DUMP_FNLIST
879
880 }
881 if (fclose (fp) != 0)
882 return RC_CORRUPT;
883
884 return 0;
885 }
886