📄 cmds.c
字号:
path = getval("download"); if (*suffix && path) { path = getval("incomplete"); /* if no incomplete path given, use CWD, not HOME */ if (path) { path = home_file(path); } else { path = strdup("."); } r = move_to_dir(win, task, path, &lfn); free(path); if (r==-1) { free(lfn); lfn = strdup(task->lfn); } } else { lfn = strdup(task->lfn); } wp(win, "* Download saved as \"%s\".\n", lfn); drw(win); free(lfn); return;}/* add some information to an incomplete file, so that we can later do a proper "resume" on that file (if that is ever implemented). The idea is to append to the end of the file one or several MD5 hashes. If a remote client's (valid) MD5 hash was reported by the server, we add that. If the file is long enough to calculate our own MD5 hashes, then we add them too - two of them in fact, a hash of the first 299008 bytes, and one of the first 300032 bytes, since it is unclear which one a remote client is going to be using. (We add these hashes so that we don't have to do it each time we are looking for a match for a potential resume. Task is still IN_PROGRESS. *//* some care needs to be taken because most OpenNap servers do not send valid hashes; rather they send the string 0000...(32 times) as the "checksum". Of course one should not base a resume on such a hash. *//* In the future, when downloading a file, we will check if any of the hashes in the incomplete directory match, and if so, treat it as a resume, if not, not. In the far future, we'll also be able to request a resume for a specific incomplete file and (gasp) maybe even have an autoresume command. */void mark_incomplete(WINDOW *win, download_t *task) { mhdr_t *m; itag_t itag; int i; itag.n = 0; itag.fn = task->fn; if (task->pos >= 300032) { /* we have a chance to calculate a valid md5 ourselves. */ m = filestats(task->lfn); if (m) { if (m->sz1 >= 300032) { itag.hash[itag.n] = strdup(m->check); itag.n++; } free(m); } } if (task->check && strncmp(task->check, "00000000", 8)) { /* have a valid checksum from remote client */ itag.hash[itag.n] = strdup(task->check); itag.n++; } if (itag.n > 0) { tagincompletefile(task->lfn, &itag); } for (i=0; i<itag.n; i++) { free(itag.hash[i]); }}/* attach an "incomplete tag" at the end of a file. The content of the * incomplete tag is that of the itag_s structure, minus the "lfn" and * "next" fields. The point is to attach one or several md5 checksums * to incomplete files, for convenient reference when resuming later. * * The format of the tag is: INCTAG_START size n 0 hash1 [ 0 hash2 [ 0 * hash3 ]] 0 fn 0 n size INCTAG_END, where INCTAG_START and * INCTAG_END are defined in defines.h, and size is a 4-byte unsigned * integer in big-endian byte order equal to the size of the entire * tag. n is the number of hashes represented, as a single byte. Note * that strings are terminated by 0 on both ends, and we give the size * and n twice; this way, the tag is conveniently scannable both * forward and backward. **/void tagincompletefile(char *fn, itag_t *itag) { int size; char sz[4]; FILE *f; int i; size = 12 + strlen(INCTAG_START) + strlen(INCTAG_END) + strlen(itag->fn); for (i=0; i<itag->n; i++) { size += 1 + strlen(itag->hash[i]); } sz[0] = (size >> 24) & 0xff; sz[1] = (size >> 16) & 0xff; sz[2] = (size >> 8) & 0xff; sz[3] = (size >> 0) & 0xff; f = fopen(fn, "ab"); if (!f) { return; /* fail silently, since there is not much we can do */ } fputs(INCTAG_START, f); fwrite(sz, 4, 1, f); fputc(itag->n, f); for (i=0; i<itag->n; i++) { fputc(0, f); fputs(itag->hash[i], f); } fputc(0, f); fputs(itag->fn, f); fputc(0, f); fputc(itag->n, f); fwrite(sz, 4, 1, f); fputs(INCTAG_END, f); fclose(f);}void adduser(chans_t *c, char *nm, int scount, unsigned char conn){ user_t *cur, *cur1 = NULL; if (!c) return; if (!c->users) { c->users = (user_t *)malloc(sizeof(user_t)); cur = c->users; } else { for (cur=c->users;cur;cur=cur->next) { if (!strcasecmp(cur->nm, nm)) return; cur1 = cur; } cur = (user_t *)malloc(sizeof(user_t)); cur1->next = cur; } cur->nm = strdup(nm); cur->addr = NULL; cur->conn = conn; cur->scount = scount; cur->flag = 0; cur->next = NULL;}/* delete a user from a channel */void deluser(chans_t *c, char *nm){ user_t *cur, *cur1 = NULL; for (cur=c->users;;cur=cur->next) { if (!cur) return; if (!strcasecmp(cur->nm, nm)) break; cur1 = cur; } if (cur1) cur1->next = cur->next; else if (cur->next) c->users = cur->next; else c->users = NULL; free(cur->nm); if (cur->addr) free(cur->addr); free(cur);}/* find user in a channel or return NULL if not found */user_t *finduser(chans_t *c, char *nm){ user_t *cur; if (!nm) return(NULL); for (cur=c->users;;cur=cur->next) { if (!cur) return(NULL); if (!strcasecmp(cur->nm, nm)) return(cur); }}/* a callback function for timer to run a user command */void timed_command(void *data) { char *cmd = (char *)data; int s; sock_t *sk; wp(wchan, ""BRIGHT(BLUE)"* Timed event: "WHITE"%s\n", cmd); drw(wchan); sk = findsock("server"); s = sk ? sk->fd : -1; parseout(s, cmd, wchan);}/* find an unused filename based on path/fn/suffix. Return 0 if the filename was unchanged, >=1 if it was changed. Set *lfn to the new full filename, and set *newfn to the new short filename. Note: we do not check whether a file by the given name can actually be created (directory exists, is writable, etc) - we simply return the first filename which does not yet exist. */int unused_filename(const char *path, const char *fn, const char *suffix, char **lfn, char **newfn) { char *fullname = NULL; char *newname = NULL; int n; char *q; struct stat st; char *cpath, *base, *suf; cpath = strdup(path); if (cpath[strlen(cpath)-1] == '/') { cpath[strlen(cpath)-1] = 0; /* cosmetic */ } base = strdup(fn); if (suffix && *suffix) { suf = strdup(suffix); } else { q = strrchr(base, '.'); if (q) { suf = strdup(q); *q = 0; } else { suf = strdup(""); } } msprintf(&newname, "%s%s", base, suf); msprintf(&fullname, "%s/%s", cpath, newname); n=0; while (stat(fullname, &st)==0) { n++; msprintf(&newname, "%s-%d%s", base, n, suf); msprintf(&fullname, "%s/%s", cpath, newname); } free(cpath); free(base); free(suf); /* success */ if (lfn) { *lfn = fullname; } else { free(fullname); } if (newfn) { *newfn = newname; } else { free(newname); } return n;}/* Opens a new file, whose name is based on fn, in the incomplete directory || download directory || cwd. The file is renamed if a file of the exact name already exists. Returns the file *f and its name *lfn. Returns 0 on success, -1 on failure with errno set. In case -1 is returned, *lfn is also set to the unsuccessful filename. */int opendownloadfile(char *fn, FILE **f, char **lfn) { char *path; int r; char *suffix = getval("incompletesuffix"); if (!suffix) { suffix = ".incomplete"; } path = getval("incomplete"); if (!path) { path = getval("download"); } /* if no download path given, use CWD, not HOME */ if (path) { path = home_file(path); } else { path = strdup("."); } r = unused_filename(path, fn, suffix, lfn, NULL); free(path); *f = fopen(*lfn, "w"); if (!*f) { return(-1); } return(0);}/* connects to a napigator-style metaserver and sets the "servers" user variable. Return -1, with *errmsg set, on error. Return number of servers found on success. */int metaserver(char *url, int timeout, const char **errmsg) { FILE *f; char *servers; int i, cnt; char **tok; char *b, *p; int count; /* number of servers */ f = open_url(url, timeout, errmsg); if (!f) return -1; servers = strdup(""); count = 0; /* parse each line of the file */ while ((b = nap_getline(f)) != NULL) { char *ip = NULL; char *port = NULL; /* tokenize line */ tok = form_tokso(b, &cnt); free(b); /* check the format of the line just read. */ if (cnt == 7) { /* If it consists of 7 tokens, it's in the old server list format. The first token is the IP address, the second is the port. */ ip = tok[0]; port = tok[1]; } else if (cnt == 14) { /* If it consists of 14 tokens, it's in the new server list format; the second token is the IP address and the third is the port. */ ip = tok[1]; port = tok[2]; } else { goto nextline; } for (p=ip; *p; p++) { if ((*p < '0' || *p > '9') && *p != '.') goto nextline; } for (p=port; *p; p++) { if (*p < '0' || *p > '9') goto nextline; } servers = realloc(servers, strlen(servers)+strlen(ip)+strlen(port)+3); if (servers[0] != 0) strcat(servers, ";"); strcat(servers, ip); strcat(servers, ":"); strcat(servers, port); count++; nextline: for (i=0; i<cnt; i++) { free(tok[i]); } free(tok); } /* while */ fclose(f); if (!count) { *errmsg = "invalid server list"; free(servers); return -1; } chset("servers", servers); free(servers); return count;}/* decompose url into host, port, and path. Return 0 on success. Return allocated strings. Pointers may be NULL, in which case the corresponding result is not returned. */int decompose_url(const char *url, char **hostp, int *portp, char **pathp) { char *p; char *host; char *path; int port; if (strncmp(url, "http://", 7) == 0) { url += 7; } p = strchr(url, '/'); if (!p) { path = strdup("/"); host = strdup(url); } else { path = strdup(p); host = strdup(url); host[p-url] = 0; } p = strchr(host, ':'); if (p) { *p = 0; port = atoi(p+1); } else { port = 80; /* standard HTTP port */ } if (hostp) { *hostp = host; } else { free(host); } if (portp) { *portp = port; } if (pathp) { *pathp = path; } else { free(path); } return 0;}/* opens the given http-url for reading. Skips HTTP headers. Optional "http://" at beginning of url is skipped. On error, return NULL with *errmsg set to a (static) error string. Otherwise, return file open for reading. */FILE *open_url(const char *url, int timeout, const char **errmsg) { char *path; char *host; int port; int r, s; struct sockaddr_in dst; FILE *f; char *b; int i; char **tok; int cnt; char *proxy; char *urlhost; proxy = getval("proxy"); if (proxy) { decompose_url(proxy, &host, &port, NULL); decompose_url(url, &urlhost, NULL, NULL); path = strdup(url); } else { decompose_url(url, &host, &port, &path); urlhost = strdup(host); } /* now, connect to host:port with path */ s = socket(AF_INET, SOCK_STREAM, 0); dst.sin_port = htons(port); dst.sin_family = AF_INET; r = resolve(host, &dst.sin_addr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -