"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/src/sitestore.c" (4 Feb 2006, 13772 Bytes) of archive /linux/www/sitecopy-0.16.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format (assuming markdown format). Alternatively you can here view or download the uninterpreted source code file. A member file download can also be achieved by clicking within a package contents listing on the according byte size field.

/* sitecopy, for managing remote web sites. Stored state handling routines. Copyright © 1999-2006, Joe Orton joe@manyfish.co.uk

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

include “config.h”

include <sys/stat.h>

ifdef HAVE_UNISTD_H

include <unistd.h>

endif

ifdef HAVE_STDLIB_H

include <stdlib.h>

endif

ifdef HAVE_STRING_H

include <string.h>

endif

ifdef HAVE_LIMITS_H

include <limits.h>

endif

include <ctype.h>

include <errno.h>

include <stdio.h>

include <ne_xml.h>

include <ne_dates.h>

include <ne_alloc.h>

include <ne_string.h>

include “i18n.h”

include “common.h”

include “sitesi.h”

/ Use a version in the site state file: * Bump the major number if a backwardly-incompatible change is made. /

define SITE_STATE_FILE_VERSION “1.0”

/ Used in stored.mode to indicate no mode known. /

define INVALID_MODE ((mode_t)-1)

/ Opens the storage file for writing / FILE site_open_storage_file(struct site site) { if (site->storage_file == NULL) { site->storage_file = fopen(site->infofile, “w” FOPEN_BINARY_FLAGS); } return site->storage_file; }

int site_close_storage_file(struct site *site) { int ret = fclose(site->storage_file); site->storage_file = NULL; return ret; }

/ Return escaped form of ‘filename’; any XML-unsafe characters are * escaped. / static char fn_escape(const char filename) { const unsigned char pnt = (const unsigned char )filename; char ret = ne_malloc(strlen(filename) * 3 + 1), p = ret;

do {
    if (!(isalnum(*pnt) || *pnt == '/' || *pnt == '.' || *pnt == '-') 
        || *pnt > 0x7f) {
        sprintf(p, "%%%02x", *pnt);
        p += 3;
    } else {
        *p++ = *(char *)pnt;
    }
} while (*++pnt != '\0');

*p = '\0';

return ret;

}

/ Return unescaped filename; reverse of fn_escape. / static char fn_unescape(const char filename) { const unsigned char pnt = (const unsigned char )filename; char ret = ne_malloc(strlen(filename) + 1), p = ret;

do {
    if (*pnt == '%') {
        *p = (NE_ASC2HEX(pnt[1]) << 4) & 0xf0;
        *p++ |= (NE_ASC2HEX(pnt[2]) & 0x0f);
        pnt += 2;
    } else {
        *p++ = *pnt;
    }
} while (*++pnt != '\0');

*p = '\0';

return ret;

}

/ Write out the stored state for the site. * Returns 0 on success, non-zero on error. / int site_write_stored_state(struct site site) { struct site_file current; FILE *fp = site_open_storage_file(site);

if (fp == NULL) {
return -1;
}

fprintf(fp, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(fp, "<sitestate version='" SITE_STATE_FILE_VERSION "'>\n");
fprintf(fp, "<options>\n");
fprintf(fp, " <saved-by package='" PACKAGE_NAME "'"
    " version='" PACKAGE_VERSION "'/>\n");
if (site->state_method == state_checksum) {
/* For forwards-compatibility */
fprintf(fp, " <checksum-algorithm><checksum-MD5/></checksum-algorithm>\n");
}
fprintf(fp, " <state-method><state-%s/></state-method>\n",
     (site->state_method==state_checksum)?"checksum":"timesize");
if (site->safemode) {
fprintf(fp, " <safemode/>\n");
}
fprintf(fp, " <escaped-filenames/>\n");
fprintf(fp, "</options>\n");
fprintf(fp, "<items>\n");
/* Now write out the items */
for (current = site->files; current!=NULL; current = current->next) {
char *fname;
if (!current->stored.exists) continue;
fprintf(fp, "<item>");
fprintf(fp, "<type><type-%s/></type>",
     (current->type==file_file)?"file":(
         (current->type==file_dir)?"directory":"link"));
    /* escape filenames correctly for XML. */
    fname = fn_escape(current->stored.filename);
fprintf(fp, "<filename>%s</filename>\n", fname);
    ne_free(fname);
    if (current->stored.mode != INVALID_MODE) {
        fprintf(fp, "<protection>%03o</protection>", 
                current->stored.mode); /* three-digit octal */
    }
switch (current->type) {
case file_link:
    fprintf(fp, "<linktarget>%s</linktarget>", 
         current->stored.linktarget);
    break;
case file_file:
    fprintf(fp, "<size>%" NE_FMT_OFF_T "</size>", 
        current->stored.size);
    switch (site->state_method) {
    case state_checksum: {
    char csum[33];
    ne_md5_to_ascii(current->stored.checksum, csum);
    fprintf(fp, "<checksum>%s</checksum>", csum);
    } break;
    case state_timesize:
    fprintf(fp, "<modtime>%ld</modtime>", current->stored.time);
    break;
    }
    fprintf(fp, "<ascii>%s</ascii>",
         current->stored.ascii?"<true/>":"<false/>");
    if (current->server.exists) {
    fprintf(fp, "<server-modtime>%ld</server-modtime>", 
         current->server.time);
    }
    break;
case file_dir:
    /* nothing to do */
    break;
}
fprintf(fp, "</item>\n");
}
fprintf(fp, "</items>\n");
fprintf(fp, "</sitestate>\n");
site->stored_state_method = site->state_method;
return site_close_storage_file(site);

}

/ neon ne_xml-based XML parsing /

define ELM_BASE 500

define SITE_ELM_sitestate (ELM_BASE + 1)

define SITE_ELM_options (ELM_BASE + 2)

define SITE_ELM_opt_saved_by (ELM_BASE + 3)

define SITE_ELM_opt_checksum (ELM_BASE + 4)

define SITE_ELM_opt_checksum_md5 (ELM_BASE + 5)

define SITE_ELM_opt_state_method (ELM_BASE + 6)

define SITE_ELM_opt_state_method_timesize (ELM_BASE + 7)

define SITE_ELM_opt_state_method_checksum (ELM_BASE + 8)

define SITE_ELM_items (ELM_BASE + 9)

define SITE_ELM_item (ELM_BASE + 10)

define SITE_ELM_type (ELM_BASE + 11)

define SITE_ELM_type_file (ELM_BASE + 12)

define SITE_ELM_type_directory (ELM_BASE + 13)

define SITE_ELM_type_link (ELM_BASE + 14)

define SITE_ELM_filename (ELM_BASE + 15)

define SITE_ELM_size (ELM_BASE + 16)

define SITE_ELM_modtime (ELM_BASE + 17)

define SITE_ELM_ascii (ELM_BASE + 18)

define SITE_ELM_linktarget (ELM_BASE + 19)

define SITE_ELM_checksum (ELM_BASE + 20)

define SITE_ELM_protection (ELM_BASE + 21)

define SITE_ELM_server_modtime (ELM_BASE + 22)

define SITE_ELM_true (ELM_BASE + 23)

define SITE_ELM_false (ELM_BASE + 24)

static const struct ne_xml_idmap elmmap[] = { { “”, “sitestate”, SITE_ELM_sitestate }, { “”, “options”, SITE_ELM_options }, { “”, “saved-by”, SITE_ELM_opt_saved_by }, { “”, “checksum-algorithm”, SITE_ELM_opt_checksum }, { “”, “checksum-MD5”, SITE_ELM_opt_checksum_md5 }, { “”, “state-method”, SITE_ELM_opt_state_method }, { “”, “state-timesize”, SITE_ELM_opt_state_method_timesize }, { “”, “state-checksum”, SITE_ELM_opt_state_method_checksum }, { “”, “items”, SITE_ELM_items }, { “”, “item”, SITE_ELM_item }, { “”, “type”, SITE_ELM_type }, { “”, “type-file”, SITE_ELM_type_file }, { “”, “type-directory”, SITE_ELM_type_directory }, { “”, “type-link”, SITE_ELM_type_link }, { “”, “filename”, SITE_ELM_filename }, { “”, “size”, SITE_ELM_size }, { “”, “modtime”, SITE_ELM_modtime }, { “”, “ascii”, SITE_ELM_ascii }, { “”, “linktarget”, SITE_ELM_linktarget }, { “”, “checksum”, SITE_ELM_checksum }, { “”, “protection”, SITE_ELM_protection }, { “”, “server-modtime”, SITE_ELM_server_modtime }, { “”, “true”, SITE_ELM_true }, { “”, “false”, SITE_ELM_false } };

struct site_xmldoc { ne_xml_parser parser; struct site site; / What we’ve collected so far / enum file_type type; struct file_state stored; struct file_state server; ne_buffer cdata; unsigned int truth:2; / 0: invalid, 1: true, 2: false */ };

static int start_element(void userdata, int parent, const char nspace, const char name, const char **atts) { int state = ne_xml_mapid(elmmap, NE_XML_MAPLEN(elmmap), nspace, name); struct site_xmldoc doc = userdata;

if (state)
    ne_buffer_clear(doc->cdata);

if (state == SITE_ELM_item) {
    /* Clear current stored state */
    memset(&doc->stored, 0, sizeof doc->stored);
    /* Initialize perms bits to invalid state */
    doc->stored.mode = INVALID_MODE;
}

if (state == SITE_ELM_ascii) {
    doc->truth = 0;
}

return state;

}

static int char_data(void userdata, int state, const char cdata, size_t len) { struct site_xmldoc *doc = userdata; ne_buffer_append(doc->cdata, cdata, len); return 0; }

static int end_element(void userdata, int state, const char nspace, const char name) { struct site_xmldoc doc = userdata; const char *cdata = doc->cdata->data; char err[512];

/* Dispatch Ajax */
switch (state) {
case SITE_ELM_opt_state_method_timesize:
doc->site->stored_state_method = state_timesize;
break;
case SITE_ELM_opt_state_method_checksum:
doc->site->stored_state_method = state_checksum;
break;
case SITE_ELM_type_file:
doc->type = file_file;
break;
case SITE_ELM_type_directory:
doc->type = file_dir;
break;
case SITE_ELM_type_link:
doc->type = file_link;
break;
case SITE_ELM_filename:
doc->stored.filename = fn_unescape(cdata);
break;
case SITE_ELM_checksum:
if (strlen(cdata) > 32) {
        ne_snprintf(err, sizeof err, _("Invalid checksum at line %d"),
                    ne_xml_currentline(doc->parser));
        ne_xml_set_error(doc->parser, err);
    return -1;
} else {
    /* FIXME: validate */
    ne_ascii_to_md5(cdata, doc->stored.checksum);

ifdef DEBUGGING

    {
    char tmp[33];
    ne_md5_to_ascii(doc->stored.checksum, tmp);
    NE_DEBUG(DEBUG_FILES, "Checksum recoded: [%32s]\n", tmp);
    }

endif / DEBUGGING /

}
break;
case SITE_ELM_size:
doc->stored.size = strtol(cdata, NULL, 10);
if (doc->stored.size == LONG_MAX) {
    }
break;
case SITE_ELM_protection:
doc->stored.mode = strtoul(cdata, NULL, 8);
break;
case SITE_ELM_server_modtime:
doc->server.time = strtol(cdata, NULL, 10);
if (doc->server.time == LONG_MIN || doc->server.time == LONG_MAX)
        goto overflow_err;
doc->server.exists = true;
break;
case SITE_ELM_modtime:
doc->stored.time = strtol(cdata, NULL, 10);
if (doc->stored.time == LONG_MIN || doc->stored.time == LONG_MAX)
        goto overflow_err;
break;
case SITE_ELM_true:
doc->truth = 1;
break;
case SITE_ELM_false:
doc->truth = 2;
break;
case SITE_ELM_ascii:
if (doc->truth) {
    doc->stored.ascii = doc->truth == 1;
} else {
        ne_snprintf(err, sizeof err, _("Boolean missing in 'ascii' "
                                       "at line %d"),
                    ne_xml_currentline(doc->parser));
        ne_xml_set_error(doc->parser, err);
    return -1;
}
break;
case SITE_ELM_linktarget:
doc->stored.linktarget = ne_strdup(cdata);
break;
case SITE_ELM_item: {
struct site_file *file;
doc->stored.exists = true;
file = file_set_stored(doc->type, &doc->stored, doc->site);
if (doc->server.exists) {
    file_state_copy(&file->server, &doc->server, doc->site);
}
DEBUG_DUMP_FILE_PROPS(DEBUG_FILES, file, doc->site);
}   break;
default:
break;
}

return 0;

overflow_err: ne_snprintf(err, sizeof err, _(“Size overflow (%s) in ‘%s’ at line %d”), cdata, name, ne_xml_currentline(doc->parser)); ne_xml_set_error(doc->parser, err); return -1; }

/ Read a new XML-format state storage file / static int parse_storage_file(struct site site, FILE fp) { ne_xml_parser *p; struct site_xmldoc doc = {0}; int ret;

doc.site = site;
doc.cdata = ne_buffer_create();

doc.parser = p = ne_xml_create();
ne_xml_push_handler(p, start_element, char_data, end_element, &doc);

ret = 0;
do {
char buffer[BUFSIZ];
int len;    
len = fread(buffer, 1, BUFSIZ, fp);
if (len < BUFSIZ) {
    if (feof(fp)) {
    ret = 1;
    } else if (ferror(fp)) {
    ret = -1;
    /* And don't parse anything else... */
    break;
    }
}
ne_xml_parse(p, buffer, len);
} while (ret == 0 && !ne_xml_failed(p));

if (!ne_xml_failed(p)) ne_xml_parse(p, "", 0);

if (ne_xml_failed(p)) {
site->last_error = ne_strdup(ne_xml_get_error(p));
ret = SITE_ERRORS;
} else if (ret < 0) {
site->last_error = ne_strdup(strerror(errno));
ret = SITE_ERRORS;
}

ne_xml_destroy(p);

return ret;    

}

int site_read_stored_state(struct site site) { FILE fp; int ret;

NE_DEBUG(DEBUG_FILES, "Reading info file: %s\n", site->infofile);
fp = fopen(site->infofile, "r");
if (fp == NULL) {
struct stat st;
    site->last_error = ne_strdup(strerror(errno));
ret = stat(site->infofile, &st);
if ((ret == 0) || (errno != ENOENT)) {
    /* The file exists but could not be opened for reading...
     * this is an error condition. */
    NE_DEBUG(DEBUG_FILES, "Stat failed %s\n", strerror(errno));
    return SITE_ERRORS;
} else {
    NE_DEBUG(DEBUG_FILES, "Info file doesn't exist.\n");
    return SITE_FAILED;
}
}
ret = parse_storage_file(site, fp);
fclose(fp);
return ret;

}