/* HTFWrite.c ** FILE WRITER ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTFWrite.c,v 2.42 1998/05/04 19:36:34 frystyk Exp $ ** ** This version of the stream object just writes to a C file. ** The file is assumed open and left open. ** ** Bugs: ** strings written must be less than buffer size. ** ** History: ** HFN: wrote it ** HWL: converted the caching scheme to be hierachical by taking ** AL code from Deamon ** HFN: moved cache code to HTCache module ** */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "HTFormat.h" #include "HTError.h" #include "HTAlert.h" #include "HTLib.h" #include "HTBind.h" #include "HTParse.h" #include "HTReq.h" #include "HTFWrite.h" /* Implemented here */ #define HASH_SIZE 1001 /* Tunable */ struct _HTStream { const HTStreamClass *isa; FILE * fp; BOOL leave_open; /* Close file when free? */ char * end_command; /* Command to execute */ BOOL remove_on_close; /* Remove file? */ char * filename; /* Name of file */ HTRequest * request; /* saved for callback */ HTRequestCallback * callback; }; /* ------------------------------------------------------------------------- */ PRIVATE int HTFWriter_put_character (HTStream * me, char c) { return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK; } PRIVATE int HTFWriter_put_string (HTStream * me, const char* s) { if (*s) /* For vms :-( 10/04-94 */ return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK; return HT_OK; } PRIVATE int HTFWriter_flush (HTStream * me) { return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK; } PRIVATE int HTFWriter_write (HTStream * me, const char* s, int l) { int status ; status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK ; if (l > 1 && status == HT_OK) (void) HTFWriter_flush(me); return status ; } PRIVATE int HTFWriter_free (HTStream * me) { if (me) { if (me->leave_open != YES) fclose(me->fp); else HTFWriter_flush(me); #ifdef HAVE_SYSTEM if (me->end_command) system(me->end_command); /* SECURITY HOLE!!! */ #endif if (me->callback) (*me->callback)(me->request, me->filename); if (me->remove_on_close) REMOVE(me->filename); HT_FREE(me->end_command); HT_FREE(me->filename); HT_FREE(me); } return HT_OK; } PRIVATE int HTFWriter_abort (HTStream * me, HTList * e) { if (STREAM_TRACE) HTTrace("FileWriter.. ABORTING...\n"); if (me) { if (me->leave_open != YES) fclose(me->fp); if (me->remove_on_close) REMOVE(me->filename); HT_FREE(me->end_command); HT_FREE(me->filename); HT_FREE(me); } return HT_ERROR; } PRIVATE const HTStreamClass HTFWriter = /* As opposed to print etc */ { "FileWriter", HTFWriter_flush, HTFWriter_free, HTFWriter_abort, HTFWriter_put_character, HTFWriter_put_string, HTFWriter_write }; PUBLIC HTStream * HTFWriter_new (HTRequest * request, FILE * fp, BOOL leave_open) { HTStream * me = NULL; if (!fp) { if (STREAM_TRACE)HTTrace("FileWriter.. Bad file descriptor\n"); return HTErrorStream(); } if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("HTFWriter_new"); me->isa = &HTFWriter; me->fp = fp; me->leave_open = leave_open; me->request = request; return me; } /* ------------------------------------------------------------------------- */ /* FILE WRITER ROUTINES */ /* ------------------------------------------------------------------------- */ /* ** This function tries really hard to find a non-existent filename relative ** to the path given. Returns a string that must be freed by the caller or ** NULL on error. The base must be '/' terminated which! */ PRIVATE char *get_filename (char * base, const char * url, const char * suffix) { char *path=NULL; char filename[40]; int hash=0; /* Do this until we find a name that doesn't exist */ while (1) { const char *ptr=url; for( ; *ptr; ptr++) hash = (int) ((hash * 31 + (* (unsigned char *) ptr)) % HASH_SIZE); #ifdef HAVE_GETPID sprintf(filename, "%d-%d", hash, (int) getpid()); #else sprintf(filename, "%d-%d", hash, time(NULL)); #endif StrAllocCopy(path, base); StrAllocCat(path, filename); if (suffix) StrAllocCat(path, suffix); { FILE *fp = fopen(path, "r"); if (fp) /* This file does already exist */ fclose(fp); else break; /* Got the file name */ } } if (STREAM_TRACE) HTTrace("Save file... Temporaray file `%s\'\n", path); return path; } /* Save Locally ** ------------ ** Saves a file to local disk. This can for example be used to dump ** date objects of unknown media types to local disk. The stream prompts ** for a file name for the temporary file. */ PUBLIC HTStream* HTSaveLocally (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { FILE * fp = NULL; char * filename = NULL; HTUserProfile * up = HTRequest_userProfile(request); char * tmproot = HTUserProfile_tmp(up); if (HTLib_secure()) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTSaveLocally"); return HTErrorStream(); } if (!tmproot) { if (STREAM_TRACE) HTTrace("Save File... turned off"); return HTErrorStream(); } /* Let's prompt the user for a file name for this file */ { HTAlertCallback *cbf = HTAlert_find(HT_A_PROMPT); HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request); /* ** If we found an alert handler for prompting the user then call it. ** If not then either we are in non-interactive mode or no handler ** has been registered. For now we then return a blackhole which may ** not be the best thing to do. */ if (cbf) { HTAlertPar * reply = HTAlert_newReply(); char * suffix = HTBind_getSuffix(anchor); char * deflt = get_filename(tmproot, HTAnchor_physical(anchor), suffix); if ((*cbf)(request, HT_A_PROMPT, HT_MSG_FILENAME,deflt,NULL,reply)) filename = HTAlert_replyMessage(reply); HTAlert_deleteReply(reply); HT_FREE(suffix); HT_FREE(deflt); } if (filename) { if ((fp = fopen(filename, "wb")) == NULL) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE, filename, strlen(filename),"HTSaveLocally"); HT_FREE(filename); return HTErrorStream(); } } else if (cbf) { if (STREAM_TRACE) HTTrace("Save File... No file name - error stream\n"); return HTErrorStream(); } else { if (STREAM_TRACE) HTTrace("Save File... No file name - black hole\n"); return HTBlackHole(); } } /* Now we are ready for creating the file writer stream */ if (fp) { HTStream * me = HTFWriter_new(request, fp, NO); me->filename = filename; return me; } HT_FREE(filename); return HTErrorStream(); } /* Take action using a system command ** ---------------------------------- ** Creates temporary file, writes to it and then executes system ** command (maybe an external viewer) when EOF has been reached. The ** stream finds a suitable name of the temporary file which preserves the ** suffix. This way, the system command can find out the file type from ** the name of the temporary file name. */ PUBLIC HTStream* HTSaveAndExecute (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { FILE * fp = NULL; char * filename = NULL; HTUserProfile * up = HTRequest_userProfile(request); char * tmproot = HTUserProfile_tmp(up); if (HTLib_secure()) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTSaveLocally"); return HTErrorStream(); } if (!tmproot) { if (STREAM_TRACE) HTTrace("Save File... turned off"); return HTErrorStream(); } /* Let's find a hash name for this file without asking user */ { HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request); char *suffix = HTBind_getSuffix(anchor); filename = get_filename(tmproot, HTAnchor_physical(anchor), suffix); HT_FREE(suffix); if (filename) { if ((fp = fopen(filename, "wb")) == NULL) { HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE, filename, strlen(filename),"HTSaveAndExecute"); HT_FREE(filename); return HTErrorStream(); } } else { if (STREAM_TRACE) HTTrace("Save File... No file name\n"); return HTErrorStream(); } } /* Now we are ready for creating the file writer stream */ if (fp) { HTStream * me = HTFWriter_new(request, fp, NO); me->filename = filename; if (param) { if ((me->end_command = (char *) HT_MALLOC((strlen((char *) param) + 10 + 3*strlen(filename)))) == NULL) HT_OUTOFMEM("SaveAndExecute"); sprintf (me->end_command, (char *)param, filename, filename, filename); } return me; } HT_FREE(filename); return HTErrorStream(); } /* Save and Call Back ** ------------------ ** This stream works exactly like the HTSaveAndExecute ** stream but in addition when EOF has been reached, it checks whether a ** callback function has been associated with the request object in which ** case, this callback is being called. This can be use by the ** application to do some processing after the system command ** has terminated. The callback function is called with the file name of ** the temporary file as parameter. */ PUBLIC HTStream* HTSaveAndCallback (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream) { HTStream * me = HTSaveAndExecute(request, param, input_format, output_format, output_stream); if (me) { me->callback = HTRequest_callback(request); return me; } return HTErrorStream(); }