/* MUDftp module * (c) Copyright 1997, 1998 Erwin S. Andreasen and Oliver Jowett * This code may be freely redistributable, as long as this header is left * intact. * * Thanks to: * - Jessica Boyd for the ROM version * - Dominic J. Eidson for the ROT version * - George Greer for the Circle version */ /************************************************************************** * Mindor 1.0 is copyright 2002-2004 by Shaun Mcbride * * Portions of the code were inspired by other works * * found around the internet. * * * * Please follow all previous copyrights and licenses. * * THanks to Maji for getting this to work on mindor. * **************************************************************************/ #include <stdlib.h> #include <unistd.h> #include <sys/time.h> #include <stdio.h> #include <stdarg.h> /* Define one of the below MERC: Will work for MERC, Envy, ROM, ROT CMUD: Will work for CircleMUD 3.x, bpl14 tested. */ #define MERC /* #define CMUD 1 */ #if defined(MERC) #include <string.h> #include "merc.h" #define CLOSE_DESCRIPTOR(desc,explanation) close_socket(desc) #define WRITE(desc,text) write_to_descriptor(desc->descriptor, text, 0) bool write_to_descriptor args( ( int desc, char *txt, int length ) ); #define GET_NAME(ch) ((ch)->name) #define GET_PASSWD(ch) ((ch)->pcdata->pwd) #define CRYPT(x,y) crypt(x,y) #elif defined(CMUD) #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #define CLOSE_DESCRIPTOR(d,txt) do { close_socket(d); log("ftp close socket: %s", txt); } while(0) #define WRITE(d, text) (write_to_output(text, d), 1) typedef struct char_data CHAR_DATA; typedef struct descriptor_data DESCRIPTOR_DATA; #define pString str #define free_string(x) free(x) #define args(x) x void mudftp_string_add(struct char_data *ch, char *txt) { string_add(ch->desc, txt); } #define string_add(ch, txt) mudftp_string_add(ch, txt) #endif #if !defined(NUL) #define NUL '\0' #endif #if !defined(MSL) #define MSL MAX_STRING_LENGTH #define MIL MAX_INPUT_LENGTH #endif #if !defined(MAX_PWD_LENGTH) #define MAX_PWD_LENGTH 12 #endif static void mudftp_notify(const char *fmt, ... ) { va_list va; char buf[MSL]; va_start(va, fmt); vsprintf(buf, fmt, va); va_end(va); /* Then do something with "buf", appropriate to the current MUD base */ /* E.g. send it over "Wiznet" or log it (not recommended) */ /* log_string (buf); */ } /* Called this because of conflict with ROT */ static void mudftp_str_replace(char *buf, const char *s, const char *repl) { char out_buf[MSL]; char *pc, *out; int len = strlen(s); bool found = FALSE; for (pc = buf, out = out_buf; *pc && (out-out_buf) < (MSL-len-4); ) if (!strncasecmp(pc, s, len)) { out += sprintf(out, repl); pc += len; found = TRUE; } else *out++ = *pc++; if (found) { /* don't bother copying if we did not change anything */ *out = NUL; strcpy(buf, out_buf); } } int line_count (const char *s) { int count = 0; for (; *s; s++) if (*s == '\n') count++; return count; } void greet_ftp (DESCRIPTOR_DATA *d, const char *argument) { d->connected = CON_FTP_AUTH; mudftp_notify("FTP connect from %s", d->host); } /* Authorization line: <username> <password> */ void handle_ftp_auth (DESCRIPTOR_DATA *d, const char *argument) { char name[MIL]; DESCRIPTOR_DATA *dftp, *dftp_next; CHAR_DATA *ch = NULL; argument = one_argument((char*)argument, name); /* Find the descriptor of the connected character */ for (dftp = descriptor_list; dftp; dftp=dftp->next) { if (dftp != d && dftp->character && !IS_NPC(dftp->character) && dftp->connected >= CON_PLAYING && !str_cmp(GET_NAME(dftp->character), name)) { ch = dftp->character; break; } } if (!ch || (strncmp(CRYPT(argument, GET_PASSWD(ch)), GET_PASSWD(ch), MAX_PWD_LENGTH))) { WRITE(d,"FAILED\n"); CLOSE_DESCRIPTOR(d, "FTP authorization failure"); mudftp_notify("FTP authorization for %s failed",name); return; } /* Search for old ftp connections */ for (dftp = descriptor_list; dftp; dftp=dftp_next) { dftp_next = dftp->next; if (dftp != d && (dftp->connected == CON_FTP_COMMAND || dftp->connected == CON_FTP_DATA) && !str_cmp(dftp->username, name)) CLOSE_DESCRIPTOR(dftp, "Old mudftp connection"); } d->username = str_dup(name); WRITE(d, "OK mudFTP 2.0 ready\n"); d->connected = CON_FTP_COMMAND; mudftp_notify("FTP authorization for %s@%s", name, d->host); } /* This algorithm is derived from the one supposedly used in Perl */ static const char *ftp_checksum(const char *string) { static char buf[10]; int i = strlen(string); unsigned hash = 0; while(i--) hash = hash * 33U + *string++; sprintf(buf, "%08x", hash); return buf; } static CHAR_DATA *findFTPChar(DESCRIPTOR_DATA *d) { DESCRIPTOR_DATA *dftp; for (dftp = descriptor_list; dftp; dftp=dftp->next) { if (dftp != d && dftp->character && !str_cmp(GET_NAME(dftp->character), d->username) && dftp->pString) return dftp->character; } return NULL; } static void finish_file(DESCRIPTOR_DATA *d) { unsigned long temp_file; mudftp_notify("Transfer of %s done from %s@%s", d->ftp.filename, d->username, d->host); d->connected = CON_FTP_COMMAND; /* Put the file in its rightful spot */ if (1 == sscanf(d->ftp.filename, "tmp/%lu", &temp_file)) { CHAR_DATA *ch = findFTPChar(d); if (ch && ((unsigned long) ch->desc->pString) == temp_file) { char temp[MSL]; char buf[MSL]; strcpy(temp, d->ftp.data); smash_tilde(temp); sprintf(buf, "OK %s\n", ftp_checksum(temp)); WRITE(d,buf); free_string(*ch->desc->pString); *ch->desc->pString = str_dup(temp); free_string(d->ftp.data); d->ftp.data = NULL; strcpy(buf, "@"); string_add(ch, buf); /* Finish editing */ return; } } WRITE(d,"FAILED Something went wrong\n"); free_string(d->ftp.data); free_string(d->ftp.filename); } void handle_ftp_command (DESCRIPTOR_DATA *d, const char *argument) { char arg[MIL]; const char *orig_argument = argument; argument = one_argument((char*)argument, arg); if (!str_cmp(arg, "noop")) { WRITE(d,"OK\n"); return; } mudftp_notify("FTP command: '%s' from %s@%s", orig_argument, d->username, d->host); if (!str_cmp(arg, "push")) { if (d->ftp.mode != FTP_NORMAL) { WRITE(d, "ERROR Already in push mode\n"); return; } d->ftp.mode = FTP_PUSH; WRITE(d, "OK Pushing you data as it arrives\n"); return; } if (!str_cmp(arg, "stop")) { CHAR_DATA *ch = findFTPChar(d); if (!ch) WRITE(d,"FAILED\n"); else { free_string(d->ftp.data); d->ftp.data = NULL; string_add(ch,"@"); /* Finish editing */ WRITE(d,"OK\n"); } if (d->ftp.mode == FTP_PUSH_WAIT) d->ftp.mode = FTP_PUSH; return; } if (!str_cmp(arg, "put")) { argument = one_argument((char*)argument, arg); if (!argument[0] || !is_number((char*)argument) || atoi(argument) < 0) WRITE(d, "ERROR Missing filename or number of lines\n"); else { d->ftp.filename = str_dup(arg); d->ftp.lines_left = atoi(argument); d->ftp.data = str_dup(""); if (d->ftp.lines_left) d->connected = CON_FTP_DATA; else finish_file(d); } if (d->ftp.mode == FTP_PUSH_WAIT) d->ftp.mode = FTP_PUSH; } else if (!str_cmp(arg, "get")) { unsigned long temp_file; if (d->ftp.mode == FTP_PUSH_WAIT) { WRITE(d, "FAILED Expected STOP or PUT"); return; } /* Send buffer being edited */ if (1 == sscanf(argument, "tmp/%lu", &temp_file)) { CHAR_DATA *ch = findFTPChar(d); if (!ch || ((unsigned long) ch->desc->pString) != temp_file) WRITE(d,"FAILED\n"); else { /* Write the string */ char buf[MSL]; char buf2[MSL]; if (*ch->desc->pString) strcpy(buf2, *ch->desc->pString); else buf2[0] = '\0'; mudftp_str_replace(buf2, "\r", ""); sprintf(buf, "SENDING tmp/%lu %d %s\n", temp_file, line_count(buf2), ftp_checksum(buf2)); if (!WRITE(d,buf) || !WRITE(d,buf2)) { CLOSE_DESCRIPTOR(d, "FTP write failure"); return; } } } else WRITE(d, "FAILED Currently only tmp/file is supported\n"); } else if (!str_cmp(arg, "quit")) CLOSE_DESCRIPTOR(d, "Quitting"); else WRITE(d, "ERROR unknown command\n"); } void handle_ftp_data (DESCRIPTOR_DATA *d, const char *argument) { int len_data, len_argument; len_data = strlen(d->ftp.data); len_argument = strlen(argument); /* Lines that overflow the buffer are silently lost */ if (len_data + len_argument < MSL-16) { char buf[MSL]; strcpy(buf, d->ftp.data); strcpy(buf+len_data, argument); /* All strings are \n internally */ strcpy(buf+len_data+len_argument, "\n\r"); free_string(d->ftp.data); d->ftp.data = str_dup(buf); } /* All of the file received? */ if (--d->ftp.lines_left == 0) finish_file(d); } /* Try to push a string to this desc. false if we can't */ bool ftp_push(DESCRIPTOR_DATA *d) { DESCRIPTOR_DATA *m; for (m = descriptor_list; m; m=m->next) { if (m->connected == CON_FTP_COMMAND && m->ftp.mode == FTP_PUSH && !str_cmp(m->username, GET_NAME(d->character))) { char buf[MSL]; char buf2[MSL]; if (*d->pString) strcpy(buf2, *d->pString); else buf2[0] = '\0'; mudftp_str_replace(buf2, "\r", ""); /* Never send \r to clients */ sprintf(buf, "SENDING tmp/%lu %d %s\n", (unsigned long)d->pString, line_count(buf2), ftp_checksum(buf2)); if (!WRITE(m,buf) || !WRITE(m,buf2)) { CLOSE_DESCRIPTOR(m, "FTP write failure"); return FALSE; } m->ftp.mode = FTP_PUSH_WAIT; return TRUE; } } return FALSE; }