r1354: Code tidying in various places (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / support.c
blob801cc04483adfa028250f652b35e33daf0222b84
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* support.c - (non-GUI) useful routines */
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <fcntl.h>
35 #include <sys/wait.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <libxml/parser.h>
40 #include <math.h>
42 #include "global.h"
44 #include "choices.h"
45 #include "main.h"
46 #include "options.h"
47 #include "support.h"
48 #include "my_vfs.h"
49 #include "fscache.h"
50 #include "i18n.h"
51 #include "main.h"
52 #include "xml.h"
54 static GHashTable *uid_hash = NULL; /* UID -> User name */
55 static GHashTable *gid_hash = NULL; /* GID -> Group name */
57 /* Static prototypes */
58 static void MD5Transform(guint32 buf[4], guint32 const in[16]);
60 /****************************************************************
61 * EXTERNAL INTERFACE *
62 ****************************************************************/
64 /* g_object_unref() the result! */
65 XMLwrapper *xml_cache_load(const gchar *pathname)
67 static GFSCache *xml_cache = NULL;
69 if (!xml_cache)
70 xml_cache = g_fscache_new((GFSLoadFunc) xml_new, NULL, NULL);
71 return g_fscache_lookup(xml_cache, pathname);
74 /* Taking this node and each directly following node with the same name,
75 * return the one which matches the current LANG.
76 * Return the node itself if nothing matches.
78 static xmlNode *best_lang(xmlNode *first)
80 xmlNode *node = first;
81 const char *target_lang = current_lang ? current_lang : "en";
83 g_return_val_if_fail(first != NULL, NULL);
85 for (node = first->next; node; node = node->next)
87 char *lang;
89 if (node->type != XML_ELEMENT_NODE)
90 continue;
92 /* Check names match... */
93 if (strcmp(node->name, first->name))
94 return first;
96 /* Check namespaces match... */
97 if ((node->ns == NULL) != (first->ns == NULL))
98 return first;
100 if (node->ns && first->ns)
101 if (strcmp(node->ns->href, first->ns->href))
102 return first;
104 lang = xmlNodeGetLang(node);
106 if (!lang)
107 continue;
108 if (strcmp(lang, target_lang) == 0)
110 g_free(lang);
111 return node;
113 g_free(lang);
116 return first;
119 /* Return the (first) child of this node with the given name.
120 * NULL if not found.
121 * If there are several consecutive nodes with the same name but different
122 * xml:lang attributes, then the one matching the current locale is used,
123 * or the first one if none match.
125 xmlNode *get_subnode(xmlNode *node, const char *namespaceURI, const char *name)
127 for (node = node->xmlChildrenNode; node; node = node->next)
129 if (node->type != XML_ELEMENT_NODE)
130 continue;
132 if (strcmp(node->name, name))
133 continue;
135 if (node->ns == NULL || namespaceURI == NULL)
137 if (node->ns == NULL && namespaceURI == NULL)
138 return best_lang(node);
139 continue;
142 if (strcmp(node->ns->href, namespaceURI) == 0)
143 return best_lang(node);
146 return NULL;
149 /* Save doc as XML as filename, 0 on success or -1 on failure */
150 int save_xml_file(xmlDocPtr doc, const gchar *filename)
152 #if LIBXML_VERSION > 20400
153 if (xmlSaveFormatFileEnc(filename, doc, NULL, 1) < 0)
154 return 1;
155 #else
156 FILE *out;
158 out = fopen(filename, "w");
159 if (!out)
160 return 1;
162 xmlDocDump(out, doc); /* Some versions return void */
164 if (fclose(out))
165 return 1;
166 #endif
168 return 0;
171 /* Create a new SOAP message and return the document and the (empty)
172 * body node.
174 xmlDocPtr soap_new(xmlNodePtr *ret_body)
176 xmlDocPtr doc;
177 xmlNodePtr root;
178 xmlNs *env_ns;
180 doc = xmlNewDoc("1.0");
181 root = xmlNewDocNode(doc, NULL, "Envelope", NULL);
182 xmlDocSetRootElement(doc, root);
184 env_ns = xmlNewNs(root, SOAP_ENV_NS, "env");
185 xmlSetNs(root, env_ns);
187 *ret_body = xmlNewTextChild(root, env_ns, "Body", NULL);
188 xmlNewNs(*ret_body, ROX_NS, "rox");
190 return doc;
193 /* Like g_strdup, but does realpath() too (if possible) */
194 char *pathdup(const char *path)
196 char real[MAXPATHLEN];
198 g_return_val_if_fail(path != NULL, NULL);
200 if (realpath(path, real))
201 return g_strdup(real);
203 return g_strdup(path);
206 /* Join the path to the leaf (adding a / between them) and
207 * return a pointer to a buffer with the result. Buffer is valid until
208 * the next call to make_path.
210 GString *make_path(const char *dir, const char *leaf)
212 static GString *buffer = NULL;
214 if (!buffer)
215 buffer = g_string_new(NULL);
217 g_return_val_if_fail(dir != NULL, buffer);
218 g_return_val_if_fail(leaf != NULL, buffer);
220 g_string_sprintf(buffer, "%s%s%s",
221 dir,
222 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
223 leaf);
225 return buffer;
228 /* Return our complete host name for DND */
229 const char *our_host_name_for_dnd(void)
231 if (o_dnd_no_hostnames.int_value)
232 return "";
233 return our_host_name();
236 /* Return our complete host name, unconditionally */
237 const char *our_host_name(void)
239 static char *name = NULL;
241 if (!name)
243 char buffer[4096];
245 if (gethostname(buffer, 4096) == 0)
247 /* gethostname doesn't always return the full name... */
248 struct hostent *ent;
250 buffer[4095] = '\0';
251 ent = gethostbyname(buffer);
252 name = g_strdup(ent ? ent->h_name : buffer);
254 else
256 g_warning("gethostname() failed - using localhost\n");
257 name = g_strdup("localhost");
261 return name;
264 void debug_free_string(void *data)
266 g_print("Freeing string '%s'\n", (char *) data);
267 g_free(data);
270 const char *user_name(uid_t uid)
272 const char *retval;
274 if (!uid_hash)
275 uid_hash = g_hash_table_new(NULL, NULL);
277 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
279 if (!retval)
281 struct passwd *passwd;
283 passwd = getpwuid(uid);
284 retval = passwd ? g_strdup(passwd->pw_name)
285 : g_strdup_printf("[%d]", (int) uid);
286 g_hash_table_insert(uid_hash, GINT_TO_POINTER(uid),
287 (gchar *) retval);
290 return retval;
293 const char *group_name(gid_t gid)
295 const char *retval;
297 if (!gid_hash)
298 gid_hash = g_hash_table_new(NULL, NULL);
300 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
302 if (!retval)
304 struct group *group;
306 group = getgrgid(gid);
307 retval = group ? g_strdup(group->gr_name)
308 : g_strdup_printf("[%d]", (int) gid);
309 g_hash_table_insert(gid_hash, GINT_TO_POINTER(gid),
310 (gchar *) retval);
313 return retval;
316 /* Return a string in the form '23Mb' in a static buffer valid until
317 * the next call.
319 char *format_size(off_t size)
321 static char *buffer = NULL;
322 const char *units;
324 if (size >= PRETTY_SIZE_LIMIT)
326 size += 1023;
327 size >>= 10;
328 if (size >= PRETTY_SIZE_LIMIT)
330 size += 1023;
331 size >>= 10;
332 if (size >= PRETTY_SIZE_LIMIT)
334 size += 1023;
335 size >>= 10;
336 units = "Gb";
338 else
339 units = "Mb";
341 else
342 units = "K";
344 else if (size == 1)
345 units = _("byte");
346 else
347 units = _("bytes");
349 if (buffer)
350 g_free(buffer);
351 buffer = g_strdup_printf("%" SIZE_FMT " %s", size, units);
353 return buffer;
356 /* Return a string in the form '23Mb' in a static buffer valid until
357 * the next call. Aligned to the right (5 chars).
359 char *format_size_aligned(off_t size)
361 static char *buffer = NULL;
362 char units;
364 if (size >= PRETTY_SIZE_LIMIT)
366 size += 1023;
367 size >>= 10;
368 if (size >= PRETTY_SIZE_LIMIT)
370 size += 1023;
371 size >>= 10;
372 if (size >= PRETTY_SIZE_LIMIT)
374 size += 1023;
375 size >>= 10;
376 units = 'G';
378 else
379 units = 'M';
381 else
382 units = 'K';
384 else
385 units = ' ';
387 if (buffer)
388 g_free(buffer);
390 buffer = g_strdup_printf("%4" SIZE_FMT "%c", size, units);
392 return buffer;
396 * Similar to format_size(), but this one uses a double argument since
397 * unsigned long isn't wide enough on all platforms and we must be able to
398 * sum sizes above 4 GB.
400 gchar *format_double_size(double size)
402 static gchar *buf = NULL;
403 const char *units;
405 if (size >= PRETTY_SIZE_LIMIT)
407 size += 1023;
408 size /= 1024;
409 if (size >= PRETTY_SIZE_LIMIT)
411 size += 1023;
412 size /= 1024;
413 if (size >= PRETTY_SIZE_LIMIT)
415 size += 1023;
416 size /= 1024;
417 units = "Gb";
419 else
420 units = "Mb";
422 else
423 units = "K";
426 else if (size != 1)
427 units = _("bytes");
428 else
429 units = _("byte");
431 if (buf)
432 g_free(buf);
433 buf = g_strdup_printf("%.0f %s", floor(size), units);
435 return buf;
438 /* Fork and exec argv. Wait and return the child's exit status.
439 * -1 if spawn fails.
440 * Returns the error string from the command if any, or NULL on success.
441 * If the process returns a non-zero exit status without producing a message,
442 * a suitable message is created.
443 * g_free() the result.
445 char *fork_exec_wait(const char **argv)
447 int status;
448 gchar *errors = NULL;
449 GError *error = NULL;
451 if (!g_spawn_sync(NULL, (char **) argv, NULL,
452 G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
453 NULL, NULL,
454 NULL, &errors, &status, &error))
456 char *msg;
458 msg = g_strdup(error->message);
459 g_error_free(error);
460 return msg;
463 if (errors && !*errors)
465 g_free(errors);
466 errors = NULL;
469 if (!WIFEXITED(status))
471 if (!errors)
472 errors = g_strdup("(Subprocess crashed?)");
474 else if (WEXITSTATUS(status))
476 if (!errors)
477 errors = g_strdup(_("ERROR"));
480 if (errors)
481 g_strstrip(errors);
483 return errors;
486 /* If a file has this UID and GID, which permissions apply to us?
487 * 0 = User, 1 = Group, 2 = World
489 gint applicable(uid_t uid, gid_t gid)
491 int i;
493 if (uid == euid)
494 return 0;
496 if (gid == egid)
497 return 1;
499 for (i = 0; i < ngroups; i++)
501 if (supplemental_groups[i] == gid)
502 return 1;
505 return 2;
508 /* Converts a file's mode to a string. Result is a pointer
509 * to a static buffer, valid until the next call.
511 char *pretty_permissions(mode_t m)
513 static char buffer[] = "rwx,rwx,rwx/UGT";
515 buffer[0] = m & S_IRUSR ? 'r' : '-';
516 buffer[1] = m & S_IWUSR ? 'w' : '-';
517 buffer[2] = m & S_IXUSR ? 'x' : '-';
519 buffer[4] = m & S_IRGRP ? 'r' : '-';
520 buffer[5] = m & S_IWGRP ? 'w' : '-';
521 buffer[6] = m & S_IXGRP ? 'x' : '-';
523 buffer[8] = m & S_IROTH ? 'r' : '-';
524 buffer[9] = m & S_IWOTH ? 'w' : '-';
525 buffer[10] = m & S_IXOTH ? 'x' : '-';
527 buffer[12] = m & S_ISUID ? 'U' : '-';
528 buffer[13] = m & S_ISGID ? 'G' : '-';
529 #ifdef S_ISVTX
530 buffer[14] = m & S_ISVTX ? 'T' : '-';
531 buffer[15] = 0;
532 #else
533 buffer[14] = 0;
534 #endif
536 return buffer;
539 /* Gets the canonical name for address and compares to our_host_name() */
540 static gboolean is_local_address(char *address)
542 struct hostent *ent;
544 ent = gethostbyname(address);
546 return strcmp(our_host_name(), ent ? ent->h_name : address) == 0;
549 /* Convert a URI to a local pathname (or NULL if it isn't local).
550 * The returned pointer points inside the input string.
551 * Possible formats:
552 * /path
553 * ///path
554 * //host/path
555 * file://host/path
557 const char *get_local_path(const char *uri)
559 if (*uri == '/')
561 char *path, *uri_host;
563 if (uri[1] != '/')
564 return uri; /* Just a local path - no host part */
566 path = strchr(uri + 2, '/');
567 if (!path)
568 return NULL; /* //something */
570 if (path - uri == 2)
571 return path; /* ///path */
573 uri_host = g_strndup(uri + 2, path - uri - 2);
574 if (is_local_address(uri_host))
576 g_free(uri_host);
577 return path; /* //myhost/path */
579 g_free(uri_host);
581 return NULL; /* From a different host */
583 else
585 if (strncasecmp(uri, "file:", 5))
586 return NULL; /* Don't know this format */
588 uri += 5;
590 if (*uri == '/')
591 return get_local_path(uri);
593 return NULL;
597 /* Set the close-on-exec flag for this FD.
598 * TRUE means that an exec()'d process will not get the FD.
600 void close_on_exec(int fd, gboolean close)
602 if (fcntl(fd, F_SETFD, close))
603 g_warning("fcntl() failed: %s\n", g_strerror(errno));
606 void set_blocking(int fd, gboolean blocking)
608 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
609 g_warning("fcntl() failed: %s\n", g_strerror(errno));
612 /* Format this time nicely.
613 * g_free() the result.
615 char *pretty_time(time_t *time)
617 static char time_buf[32];
619 if (strftime(time_buf, sizeof(time_buf),
620 TIME_FORMAT, localtime(time)) == 0)
621 time_buf[0]= 0;
623 return to_utf8(time_buf);
626 #ifndef O_NOFOLLOW
627 # define O_NOFOLLOW 0x0
628 #endif
630 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
631 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
632 * do the copy manually using vfs.
634 * Returns an error string, or NULL on success. g_free() the result.
636 * XXX: This was only used for libvfs...
638 guchar *copy_file(const guchar *from, const guchar *to)
640 const char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
642 argv[2] = from;
643 argv[3] = to;
645 return fork_exec_wait(argv);
648 /* 'word' has all special characters escaped so that it may be inserted
649 * into a shell command.
650 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
652 guchar *shell_escape(const guchar *word)
654 GString *tmp;
655 guchar *retval;
657 tmp = g_string_new(NULL);
659 while (*word)
661 if (strchr(" ?*['\"$~\\|();!`&", *word))
662 g_string_append_c(tmp, '\\');
663 g_string_append_c(tmp, *word);
664 word++;
667 retval = tmp->str;
668 g_string_free(tmp, FALSE);
669 return retval;
672 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
673 * (or the two are the same item/directory).
674 * FALSE if parent doesn't exist.
676 gboolean is_sub_dir(const char *sub_obj, const char *parent)
678 struct stat parent_info;
679 char *sub;
681 if (mc_lstat(parent, &parent_info))
682 return FALSE; /* Parent doesn't exist */
684 /* For checking Copy/Move operations do a realpath first on sub
685 * (the destination), since copying into a symlink is the same as
686 * copying into the thing it points to. Don't realpath 'parent' though;
687 * copying a symlink just makes a new symlink.
689 * When checking if an icon depends on a file (parent), use realpath on
690 * sub (the icon) too.
692 sub = pathdup(sub_obj);
694 while (1)
696 char *slash;
697 struct stat info;
699 if (mc_lstat(sub, &info) == 0)
701 if (info.st_dev == parent_info.st_dev &&
702 info.st_ino == parent_info.st_ino)
704 g_free(sub);
705 return TRUE;
709 slash = strrchr(sub, '/');
710 if (!slash)
711 break;
712 if (slash == sub)
714 if (sub[1])
715 sub[1] = '\0';
716 else
717 break;
719 else
720 *slash = '\0';
723 g_free(sub);
725 return FALSE;
728 /* True if the string 'list' contains 'item'.
729 * Eg ("close", "close, help") -> TRUE
731 gboolean in_list(const guchar *item, const guchar *list)
733 int len;
735 len = strlen(item);
737 while (*list)
739 if (strncmp(item, list, len) == 0 && !isalpha(list[len]))
740 return TRUE;
741 list = strchr(list, ',');
742 if (!list)
743 return FALSE;
744 while (isspace(*++list))
748 return FALSE;
751 /* Split a path into its components. Eg:
753 * /bob/fred -> ["bob", "fred"]
754 * ///a//b// -> ["a", "b"]
755 * / -> []
757 * The array and the strings in it must be freed after use.
759 GPtrArray *split_path(const guchar *path)
761 GPtrArray *array;
762 guchar *slash;
764 g_return_val_if_fail(path != NULL, NULL);
766 array = g_ptr_array_new();
768 while (1)
770 while (path[0] == '/')
771 path++;
772 if (path[0] == '\0')
773 break;
775 slash = strchr(path, '/');
776 if (slash)
778 g_ptr_array_add(array, g_strndup(path, slash - path));
779 path = slash + 1;
780 continue;
782 g_ptr_array_add(array, g_strdup(path));
783 break;
786 return array;
789 /* Return the shortest path from 'from' to 'to'.
790 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
792 guchar *get_relative_path(const guchar *from, const guchar *to)
794 GString *path;
795 guchar *retval;
796 GPtrArray *src, *dst;
797 int i, j;
799 src = split_path(from);
800 dst = split_path(to);
802 /* The last component of src doesn't matter... */
803 if (src->len)
805 g_free(src->pdata[src->len - 1]);
806 g_ptr_array_remove_index(src, src->len - 1);
809 /* Strip off common path elements... */
810 i = 0;
811 while (i < src->len && i < dst->len)
813 guchar *a = (guchar *) src->pdata[i];
814 guchar *b = (guchar *) dst->pdata[i];
816 if (strcmp(a, b) != 0)
817 break;
818 i++;
821 /* Go up one dir for each element remaining in src */
822 path = g_string_new(NULL);
823 for (j = i; j < src->len; j++)
824 g_string_append(path, "../");
826 /* Go down one dir for each element remaining in dst */
827 for (j = i; j < dst->len; j++)
829 g_string_append(path, (guchar *) dst->pdata[j]);
830 g_string_append_c(path, '/');
833 if (path->str[path->len - 1] == '/')
834 g_string_truncate(path, path->len - 1);
835 if (path->len == 0)
836 g_string_assign(path, ".");
838 /* Free the arrays */
839 for (i = 0; i < src->len; i++)
840 g_free(src->pdata[i]);
841 g_ptr_array_free(src, TRUE);
842 for (i = 0; i < dst->len; i++)
843 g_free(dst->pdata[i]);
844 g_ptr_array_free(dst, TRUE);
846 retval = path->str;
847 g_string_free(path, FALSE);
849 return retval;
853 * Interperet text as a boolean value. Return defvalue if we don't
854 * recognise it
856 int text_to_boolean(const char *text, int defvalue)
858 if(g_strcasecmp(text, "true")==0)
859 return TRUE;
860 else if(g_strcasecmp(text, "false")==0)
861 return FALSE;
862 else if(g_strcasecmp(text, "yes")==0)
863 return TRUE;
864 else if(g_strcasecmp(text, "no")==0)
865 return FALSE;
866 else if(isdigit(text[0]))
867 return !!atoi(text);
869 return defvalue;
872 /* Return the pathname that this symlink points to.
873 * NULL on error (not a symlink, path too long) and errno set.
874 * g_free() the result.
876 char *readlink_dup(const char *source)
878 char path[MAXPATHLEN + 1];
879 int got;
881 got = readlink(source, path, MAXPATHLEN);
882 if (got < 0 || got > MAXPATHLEN)
883 return NULL;
885 return g_strndup(path, got);
889 * This code implements the MD5 message-digest algorithm.
890 * The algorithm is due to Ron Rivest. The original code was
891 * written by Colin Plumb in 1993, and put in the public domain.
893 * Modified to use glib datatypes. Put under GPL to simplify
894 * licensing for ROX-Filer. Taken from Debian's dpkg package.
897 #define md5byte unsigned char
899 typedef struct _MD5Context MD5Context;
901 struct _MD5Context {
902 guint32 buf[4];
903 guint32 bytes[2];
904 guint32 in[16];
907 #if G_BYTE_ORDER == G_BIG_ENDIAN
908 void byteSwap(guint32 *buf, unsigned words)
910 md5byte *p = (md5byte *)buf;
912 do {
913 *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
914 ((unsigned)p[1] << 8 | p[0]);
915 p += 4;
916 } while (--words);
918 #else
919 #define byteSwap(buf,words)
920 #endif
923 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
924 * initialization constants.
926 static void MD5Init(MD5Context *ctx)
928 ctx->buf[0] = 0x67452301;
929 ctx->buf[1] = 0xefcdab89;
930 ctx->buf[2] = 0x98badcfe;
931 ctx->buf[3] = 0x10325476;
933 ctx->bytes[0] = 0;
934 ctx->bytes[1] = 0;
938 * Update context to reflect the concatenation of another buffer full
939 * of bytes.
941 static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
943 guint32 t;
945 /* Update byte count */
947 t = ctx->bytes[0];
948 if ((ctx->bytes[0] = t + len) < t)
949 ctx->bytes[1]++; /* Carry from low to high */
951 t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
952 if (t > len) {
953 memcpy((md5byte *)ctx->in + 64 - t, buf, len);
954 return;
956 /* First chunk is an odd size */
957 memcpy((md5byte *)ctx->in + 64 - t, buf, t);
958 byteSwap(ctx->in, 16);
959 MD5Transform(ctx->buf, ctx->in);
960 buf += t;
961 len -= t;
963 /* Process data in 64-byte chunks */
964 while (len >= 64) {
965 memcpy(ctx->in, buf, 64);
966 byteSwap(ctx->in, 16);
967 MD5Transform(ctx->buf, ctx->in);
968 buf += 64;
969 len -= 64;
972 /* Handle any remaining bytes of data. */
973 memcpy(ctx->in, buf, len);
977 * Final wrapup - pad to 64-byte boundary with the bit pattern
978 * 1 0* (64-bit count of bits processed, MSB-first)
979 * Returns the newly allocated string of the hash.
981 static char *MD5Final(MD5Context *ctx)
983 char *retval;
984 int i;
985 int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
986 md5byte *p = (md5byte *)ctx->in + count;
987 guint8 *bytes;
989 /* Set the first char of padding to 0x80. There is always room. */
990 *p++ = 0x80;
992 /* Bytes of padding needed to make 56 bytes (-8..55) */
993 count = 56 - 1 - count;
995 if (count < 0) { /* Padding forces an extra block */
996 memset(p, 0, count + 8);
997 byteSwap(ctx->in, 16);
998 MD5Transform(ctx->buf, ctx->in);
999 p = (md5byte *)ctx->in;
1000 count = 56;
1002 memset(p, 0, count);
1003 byteSwap(ctx->in, 14);
1005 /* Append length in bits and transform */
1006 ctx->in[14] = ctx->bytes[0] << 3;
1007 ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
1008 MD5Transform(ctx->buf, ctx->in);
1010 byteSwap(ctx->buf, 4);
1012 retval = g_malloc(33);
1013 bytes = (guint8 *) ctx->buf;
1014 for (i = 0; i < 16; i++)
1015 sprintf(retval + (i * 2), "%02x", bytes[i]);
1016 retval[32] = '\0';
1018 return retval;
1021 # ifndef ASM_MD5
1023 /* The four core functions - F1 is optimized somewhat */
1025 /* #define F1(x, y, z) (x & y | ~x & z) */
1026 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1027 #define F2(x, y, z) F1(z, x, y)
1028 #define F3(x, y, z) (x ^ y ^ z)
1029 #define F4(x, y, z) (y ^ (x | ~z))
1031 /* This is the central step in the MD5 algorithm. */
1032 #define MD5STEP(f,w,x,y,z,in,s) \
1033 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
1036 * The core of the MD5 algorithm, this alters an existing MD5 hash to
1037 * reflect the addition of 16 longwords of new data. MD5Update blocks
1038 * the data and converts bytes into longwords for this routine.
1040 static void MD5Transform(guint32 buf[4], guint32 const in[16])
1042 register guint32 a, b, c, d;
1044 a = buf[0];
1045 b = buf[1];
1046 c = buf[2];
1047 d = buf[3];
1049 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
1050 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
1051 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
1052 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
1053 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
1054 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
1055 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
1056 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
1057 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
1058 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
1059 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
1060 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
1061 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
1062 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
1063 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
1064 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
1066 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
1067 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
1068 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
1069 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
1070 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
1071 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
1072 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
1073 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
1074 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
1075 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
1076 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
1077 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
1078 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
1079 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1080 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1081 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1083 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1084 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1085 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1086 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1087 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1088 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1089 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1090 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1091 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1092 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1093 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1094 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1095 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1096 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1097 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1098 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1100 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1101 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1102 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1103 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1104 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1105 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1106 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1107 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1108 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1109 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1110 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1111 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1112 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1113 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1114 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1115 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1117 buf[0] += a;
1118 buf[1] += b;
1119 buf[2] += c;
1120 buf[3] += d;
1123 # endif /* ASM_MD5 */
1125 char *md5_hash(const char *message)
1127 MD5Context ctx;
1129 MD5Init(&ctx);
1130 MD5Update(&ctx, message, strlen(message));
1131 return MD5Final(&ctx);
1134 /* Convert string 'src' from the current locale to UTF-8 */
1135 gchar *to_utf8(const gchar *src)
1137 gchar *retval;
1139 if (!src)
1140 return NULL;
1142 retval = g_locale_to_utf8(src, -1, NULL, NULL, NULL);
1144 return retval ? retval : g_strdup(src);
1147 /* Convert string 'src' to the current locale from UTF-8 */
1148 gchar *from_utf8(const gchar *src)
1150 gchar *retval;
1152 if (!src)
1153 return NULL;
1155 retval = g_locale_from_utf8(src, -1, NULL, NULL, NULL);
1157 return retval ? retval : g_strdup(src);
1160 /* Removes trailing / chars and converts a leading '~/' (if any) to
1161 * the user's home dir. g_free() the result.
1163 gchar *expand_path(const gchar *path)
1165 guchar *retval;
1166 int path_len;
1168 g_return_val_if_fail(path != NULL, NULL);
1170 path_len = strlen(path);
1171 while (path_len > 1 && path[path_len - 1] == '/')
1172 path_len--;
1174 retval = g_strndup(path, path_len);
1176 if (path[0] == '~' && (path[1] == '\0' || path[1] == '/'))
1178 guchar *tmp = retval;
1180 retval = g_strconcat(home_dir, retval + 1, NULL);
1181 g_free(tmp);
1184 return retval;