r1322: Converted MaskedPixmap to GObject.
[rox-filer.git] / ROX-Filer / src / support.c
blob73cf05fffbe10c6c7cac002a26d1d7304d16f99e
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>
41 #include "global.h"
43 #include "choices.h"
44 #include "main.h"
45 #include "options.h"
46 #include "support.h"
47 #include "my_vfs.h"
48 #include "fscache.h"
49 #include "i18n.h"
50 #include "main.h"
51 #include "xml.h"
53 static GHashTable *uid_hash = NULL; /* UID -> User name */
54 static GHashTable *gid_hash = NULL; /* GID -> Group name */
56 /* Static prototypes */
57 static void MD5Transform(guint32 buf[4], guint32 const in[16]);
59 /****************************************************************
60 * EXTERNAL INTERFACE *
61 ****************************************************************/
63 /* XXX */
64 gint object_getref(GObject *object)
66 return object->ref_count;
69 /* g_fscache_data_unref() the result! */
70 XMLwrapper *xml_cache_load(const gchar *pathname)
72 static GFSCache *xml_cache = NULL;
74 if (!xml_cache)
75 xml_cache = g_fscache_new((GFSLoadFunc) xml_new,
76 (GFSRefFunc) g_object_ref,
77 (GFSRefFunc) g_object_unref,
78 (GFSGetRefFunc) object_getref,
79 NULL, NULL);
80 return g_fscache_lookup(xml_cache, pathname);
83 /* Taking this node and each directly following node with the same name,
84 * return the one which matches the current LANG.
85 * Return the node itself if nothing matches.
87 static xmlNode *best_lang(xmlNode *first)
89 xmlNode *node = first;
90 const char *target_lang = current_lang ? current_lang : "en";
92 g_return_val_if_fail(first != NULL, NULL);
94 for (node = first->next; node; node = node->next)
96 char *lang;
98 if (node->type != XML_ELEMENT_NODE)
99 continue;
101 /* Check names match... */
102 if (strcmp(node->name, first->name))
103 return first;
105 /* Check namespaces match... */
106 if ((node->ns == NULL) != (first->ns == NULL))
107 return first;
109 if (node->ns && first->ns)
110 if (strcmp(node->ns->href, first->ns->href))
111 return first;
113 lang = xmlNodeGetLang(node);
115 if (!lang)
116 continue;
117 if (strcmp(lang, target_lang) == 0)
119 g_free(lang);
120 return node;
122 g_free(lang);
125 return first;
128 /* Return the (first) child of this node with the given name.
129 * NULL if not found.
130 * If there are several consecutive nodes with the same name but different
131 * xml:lang attributes, then the one matching the current locale is used,
132 * or the first one if none match.
134 xmlNode *get_subnode(xmlNode *node, const char *namespaceURI, const char *name)
136 for (node = node->xmlChildrenNode; node; node = node->next)
138 if (node->type != XML_ELEMENT_NODE)
139 continue;
141 if (strcmp(node->name, name))
142 continue;
144 if (node->ns == NULL || namespaceURI == NULL)
146 if (node->ns == NULL && namespaceURI == NULL)
147 return best_lang(node);
148 continue;
151 if (strcmp(node->ns->href, namespaceURI) == 0)
152 return best_lang(node);
155 return NULL;
158 /* Save doc as XML as filename, 0 on success or -1 on failure */
159 int save_xml_file(xmlDocPtr doc, const gchar *filename)
161 #if LIBXML_VERSION > 20400
162 if (xmlSaveFormatFileEnc(filename, doc, NULL, 1) < 0)
163 return 1;
164 #else
165 FILE *out;
167 out = fopen(filename, "w");
168 if (!out)
169 return 1;
171 xmlDocDump(out, doc); /* Some versions return void */
173 if (fclose(out))
174 return 1;
175 #endif
177 return 0;
180 /* Create a new SOAP message and return the document and the (empty)
181 * body node.
183 xmlDocPtr soap_new(xmlNodePtr *ret_body)
185 xmlDocPtr doc;
186 xmlNodePtr root;
187 xmlNs *env_ns;
189 doc = xmlNewDoc("1.0");
190 root = xmlNewDocNode(doc, NULL, "Envelope", NULL);
191 xmlDocSetRootElement(doc, root);
193 env_ns = xmlNewNs(root, SOAP_ENV_NS, "env");
194 xmlSetNs(root, env_ns);
196 *ret_body = xmlNewTextChild(root, env_ns, "Body", NULL);
197 xmlNewNs(*ret_body, ROX_NS, "rox");
199 return doc;
202 /* Like g_strdup, but does realpath() too (if possible) */
203 char *pathdup(const char *path)
205 char real[MAXPATHLEN];
207 g_return_val_if_fail(path != NULL, NULL);
209 if (realpath(path, real))
210 return g_strdup(real);
212 return g_strdup(path);
215 /* Join the path to the leaf (adding a / between them) and
216 * return a pointer to a buffer with the result. Buffer is valid until
217 * the next call to make_path.
219 GString *make_path(const char *dir, const char *leaf)
221 static GString *buffer = NULL;
223 if (!buffer)
224 buffer = g_string_new(NULL);
226 g_return_val_if_fail(dir != NULL, buffer);
227 g_return_val_if_fail(leaf != NULL, buffer);
229 g_string_sprintf(buffer, "%s%s%s",
230 dir,
231 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
232 leaf);
234 return buffer;
237 /* Return our complete host name for DND */
238 const char *our_host_name_for_dnd(void)
240 if (o_dnd_no_hostnames.int_value)
241 return "";
242 return our_host_name();
245 /* Return our complete host name, unconditionally */
246 const char *our_host_name(void)
248 static char *name = NULL;
250 if (!name)
252 char buffer[4096];
254 if (gethostname(buffer, 4096) == 0)
256 /* gethostname doesn't always return the full name... */
257 struct hostent *ent;
259 buffer[4095] = '\0';
260 ent = gethostbyname(buffer);
261 name = g_strdup(ent ? ent->h_name : buffer);
263 else
265 g_warning("gethostname() failed - using localhost\n");
266 name = g_strdup("localhost");
270 return name;
273 /* Create a child process. cd to dir first (if dir is non-NULL).
274 * If from_stderr is set, create a pipe for stderr and return the readable
275 * side here.
276 * Returns the PID of the child, or 0 on failure (from_stderr is still valid).
278 pid_t spawn_full(const char **argv, const char *dir, int *from_stderr)
280 int child;
281 int fd[2];
283 if (from_stderr)
285 if (pipe(fd) == 0)
286 *from_stderr = fd[0];
287 else
289 *from_stderr = -1;
290 from_stderr = NULL;
294 child = fork();
296 if (child == -1)
297 return 0; /* Failure */
298 else if (child == 0)
300 /* We are the child process */
301 if (from_stderr)
303 close(fd[0]);
304 if (fd[1] != STDERR_FILENO)
306 dup2(fd[1], STDERR_FILENO);
307 close(fd[1]);
308 close_on_exec(STDERR_FILENO, FALSE);
312 if (dir)
313 if (chdir(dir))
314 fprintf(stderr, "chdir() failed: %s\n",
315 g_strerror(errno));
316 execvp(argv[0], (char **) argv);
317 fprintf(stderr, "execvp(%s, ...) failed: %s\n",
318 argv[0],
319 g_strerror(errno));
320 _exit(0);
323 if (from_stderr)
324 close(fd[1]);
326 /* We are the parent */
327 return child;
330 void debug_free_string(void *data)
332 g_print("Freeing string '%s'\n", (char *) data);
333 g_free(data);
336 const char *user_name(uid_t uid)
338 const char *retval;
340 if (!uid_hash)
341 uid_hash = g_hash_table_new(NULL, NULL);
343 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
345 if (!retval)
347 struct passwd *passwd;
349 passwd = getpwuid(uid);
350 retval = passwd ? g_strdup(passwd->pw_name)
351 : g_strdup_printf("[%d]", (int) uid);
352 g_hash_table_insert(uid_hash, GINT_TO_POINTER(uid),
353 (gchar *) retval);
356 return retval;
359 const char *group_name(gid_t gid)
361 const char *retval;
363 if (!gid_hash)
364 gid_hash = g_hash_table_new(NULL, NULL);
366 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
368 if (!retval)
370 struct group *group;
372 group = getgrgid(gid);
373 retval = group ? g_strdup(group->gr_name)
374 : g_strdup_printf("[%d]", (int) gid);
375 g_hash_table_insert(gid_hash, GINT_TO_POINTER(gid),
376 (gchar *) retval);
379 return retval;
382 /* Return a string in the form '23Mb' in a static buffer valid until
383 * the next call.
385 char *format_size(off_t size)
387 static char *buffer = NULL;
388 const char *units;
390 if (size >= PRETTY_SIZE_LIMIT)
392 size += 1023;
393 size >>= 10;
394 if (size >= PRETTY_SIZE_LIMIT)
396 size += 1023;
397 size >>= 10;
398 if (size >= PRETTY_SIZE_LIMIT)
400 size += 1023;
401 size >>= 10;
402 units = "Gb";
404 else
405 units = "Mb";
407 else
408 units = "K";
410 else if (size == 1)
411 units = _("byte");
412 else
413 units = _("bytes");
415 if (buffer)
416 g_free(buffer);
417 buffer = g_strdup_printf("%" SIZE_FMT " %s", size, units);
419 return buffer;
422 /* Return a string in the form '23Mb' in a static buffer valid until
423 * the next call. Aligned to the right (5 chars).
425 char *format_size_aligned(off_t size)
427 static char *buffer = NULL;
428 char units;
430 if (size >= PRETTY_SIZE_LIMIT)
432 size += 1023;
433 size >>= 10;
434 if (size >= PRETTY_SIZE_LIMIT)
436 size += 1023;
437 size >>= 10;
438 if (size >= PRETTY_SIZE_LIMIT)
440 size += 1023;
441 size >>= 10;
442 units = 'G';
444 else
445 units = 'M';
447 else
448 units = 'K';
450 else
451 units = ' ';
453 if (buffer)
454 g_free(buffer);
456 buffer = g_strdup_printf("%4" SIZE_FMT "%c", size, units);
458 return buffer;
462 * Similar to format_size(), but this one uses a double argument since
463 * unsigned long isn't wide enough on all platforms and we must be able to
464 * sum sizes above 4 GB.
466 gchar *format_double_size(double size)
468 static gchar *buf = NULL;
469 const char *units;
471 if (size >= PRETTY_SIZE_LIMIT)
473 size += 1023;
474 size /= 1024;
475 if (size >= PRETTY_SIZE_LIMIT)
477 size += 1023;
478 size /= 1024;
479 if (size >= PRETTY_SIZE_LIMIT)
481 size += 1023;
482 size /= 1024;
483 units = "Gb";
485 else
486 units = "Mb";
488 else
489 units = "K";
492 else if (size != 1)
493 units = _("bytes");
494 else
495 units = _("byte");
497 if (buf)
498 g_free(buf);
499 buf = g_strdup_printf("%.0f %s", size, units);
501 return buf;
504 /* Ensure that str ends with a newline (or is empty) */
505 static void newline(GString *str)
507 if (str->len && str->str[str->len - 1] != '\n')
508 g_string_append_c(str, '\n');
511 /* Fork and exec argv. Wait and return the child's exit status.
512 * -1 if spawn fails.
513 * Returns the error string from the command if any, or NULL on success.
514 * If the process returns a non-zero exit status without producing a message,
515 * a suitable message is created.
516 * g_free() the result.
518 char *fork_exec_wait(const char **argv)
520 pid_t child;
521 int status = -1;
522 GString *errors;
523 char buffer[257];
524 int from_stderr;
526 errors = g_string_new(NULL);
528 child = spawn_full(argv, NULL, &from_stderr);
530 if (!child)
532 newline(errors);
533 g_string_append(errors, "fork: ");
534 g_string_append(errors, g_strerror(errno));
535 goto out;
538 while (from_stderr != -1)
540 int got;
542 got = read(from_stderr, buffer, sizeof(buffer) - 1);
543 if (got < 0)
545 newline(errors);
546 g_string_append(errors, "read: ");
547 g_string_append(errors, g_strerror(errno));
549 if (got <= 0)
550 break;
551 buffer[got] = '\0';
552 g_string_append(errors, buffer);
555 while (1)
557 if (waitpid(child, &status, 0) == -1)
559 if (errno != EINTR)
561 newline(errors);
562 g_string_append(errors, "waitpid: ");
563 g_string_append(errors, g_strerror(errno));
564 break;
567 else
569 if (!WIFEXITED(status))
571 newline(errors);
572 g_string_append(errors, "(crashed?)");
574 else if (WEXITSTATUS(status))
576 newline(errors);
577 if (!errors->len)
578 g_string_append(errors, "ERROR");
580 break;
584 out:
585 if (from_stderr != -1)
586 close(from_stderr);
588 if (errors->len && errors->str[errors->len - 1] == '\n')
589 g_string_truncate(errors, errors->len - 1);
591 if (errors->len)
593 char *retval = errors->str;
594 g_string_free(errors, FALSE);
595 return retval;
598 return NULL;
601 /* If a file has this UID and GID, which permissions apply to us?
602 * 0 = User, 1 = Group, 2 = World
604 gint applicable(uid_t uid, gid_t gid)
606 int i;
608 if (uid == euid)
609 return 0;
611 if (gid == egid)
612 return 1;
614 for (i = 0; i < ngroups; i++)
616 if (supplemental_groups[i] == gid)
617 return 1;
620 return 2;
623 /* Converts a file's mode to a string. Result is a pointer
624 * to a static buffer, valid until the next call.
626 char *pretty_permissions(mode_t m)
628 static char buffer[] = "rwx,rwx,rwx/UGT";
630 buffer[0] = m & S_IRUSR ? 'r' : '-';
631 buffer[1] = m & S_IWUSR ? 'w' : '-';
632 buffer[2] = m & S_IXUSR ? 'x' : '-';
634 buffer[4] = m & S_IRGRP ? 'r' : '-';
635 buffer[5] = m & S_IWGRP ? 'w' : '-';
636 buffer[6] = m & S_IXGRP ? 'x' : '-';
638 buffer[8] = m & S_IROTH ? 'r' : '-';
639 buffer[9] = m & S_IWOTH ? 'w' : '-';
640 buffer[10] = m & S_IXOTH ? 'x' : '-';
642 buffer[12] = m & S_ISUID ? 'U' : '-';
643 buffer[13] = m & S_ISGID ? 'G' : '-';
644 #ifdef S_ISVTX
645 buffer[14] = m & S_ISVTX ? 'T' : '-';
646 buffer[15] = 0;
647 #else
648 buffer[14] = 0;
649 #endif
651 return buffer;
654 /* Gets the canonical name for address and compares to our_host_name() */
655 static gboolean is_local_address(char *address)
657 struct hostent *ent;
659 ent = gethostbyname(address);
661 return strcmp(our_host_name(), ent ? ent->h_name : address) == 0;
664 /* Convert a URI to a local pathname (or NULL if it isn't local).
665 * The returned pointer points inside the input string.
666 * Possible formats:
667 * /path
668 * ///path
669 * //host/path
670 * file://host/path
672 const char *get_local_path(const char *uri)
674 if (*uri == '/')
676 char *path, *uri_host;
678 if (uri[1] != '/')
679 return uri; /* Just a local path - no host part */
681 path = strchr(uri + 2, '/');
682 if (!path)
683 return NULL; /* //something */
685 if (path - uri == 2)
686 return path; /* ///path */
688 uri_host = g_strndup(uri + 2, path - uri - 2);
689 if (is_local_address(uri_host))
691 g_free(uri_host);
692 return path; /* //myhost/path */
694 g_free(uri_host);
696 return NULL; /* From a different host */
698 else
700 if (strncasecmp(uri, "file:", 5))
701 return NULL; /* Don't know this format */
703 uri += 5;
705 if (*uri == '/')
706 return get_local_path(uri);
708 return NULL;
712 /* Set the close-on-exec flag for this FD.
713 * TRUE means that an exec()'d process will not get the FD.
715 void close_on_exec(int fd, gboolean close)
717 if (fcntl(fd, F_SETFD, close))
718 g_warning("fcntl() failed: %s\n", g_strerror(errno));
721 void set_blocking(int fd, gboolean blocking)
723 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
724 g_warning("fcntl() failed: %s\n", g_strerror(errno));
727 /* Format this time nicely. The result is a pointer to a static buffer,
728 * valid until the next call.
730 char *pretty_time(time_t *time)
732 static char time_buf[32];
734 if (strftime(time_buf, sizeof(time_buf),
735 TIME_FORMAT, localtime(time)) == 0)
736 time_buf[0]= 0;
738 return time_buf;
741 #ifndef O_NOFOLLOW
742 # define O_NOFOLLOW 0x0
743 #endif
745 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
746 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
747 * do the copy manually using vfs.
749 * Returns an error string, or NULL on success. g_free() the result.
751 * XXX: This was only used for libvfs...
753 guchar *copy_file(const guchar *from, const guchar *to)
755 const char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
757 argv[2] = from;
758 argv[3] = to;
760 return fork_exec_wait(argv);
763 /* 'word' has all special characters escaped so that it may be inserted
764 * into a shell command.
765 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
767 guchar *shell_escape(const guchar *word)
769 GString *tmp;
770 guchar *retval;
772 tmp = g_string_new(NULL);
774 while (*word)
776 if (strchr(" ?*['\"$~\\|();!`&", *word))
777 g_string_append_c(tmp, '\\');
778 g_string_append_c(tmp, *word);
779 word++;
782 retval = tmp->str;
783 g_string_free(tmp, FALSE);
784 return retval;
787 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
788 * (or the two are the same item/directory).
789 * FALSE if parent doesn't exist.
791 gboolean is_sub_dir(const char *sub_obj, const char *parent)
793 struct stat parent_info;
794 char *sub;
796 if (mc_lstat(parent, &parent_info))
797 return FALSE; /* Parent doesn't exist */
799 /* For checking Copy/Move operations do a realpath first on sub
800 * (the destination), since copying into a symlink is the same as
801 * copying into the thing it points to. Don't realpath 'parent' though;
802 * copying a symlink just makes a new symlink.
804 * When checking if an icon depends on a file (parent), use realpath on
805 * sub (the icon) too.
807 sub = pathdup(sub_obj);
809 while (1)
811 char *slash;
812 struct stat info;
814 if (mc_lstat(sub, &info) == 0)
816 if (info.st_dev == parent_info.st_dev &&
817 info.st_ino == parent_info.st_ino)
819 g_free(sub);
820 return TRUE;
824 slash = strrchr(sub, '/');
825 if (!slash)
826 break;
827 if (slash == sub)
829 if (sub[1])
830 sub[1] = '\0';
831 else
832 break;
834 else
835 *slash = '\0';
838 g_free(sub);
840 return FALSE;
843 /* True if the string 'list' contains 'item'.
844 * Eg ("close", "close, help") -> TRUE
846 gboolean in_list(const guchar *item, const guchar *list)
848 int len;
850 len = strlen(item);
852 while (*list)
854 if (strncmp(item, list, len) == 0 && !isalpha(list[len]))
855 return TRUE;
856 list = strchr(list, ',');
857 if (!list)
858 return FALSE;
859 while (isspace(*++list))
863 return FALSE;
866 /* Split a path into its components. Eg:
868 * /bob/fred -> ["bob", "fred"]
869 * ///a//b// -> ["a", "b"]
870 * / -> []
872 * The array and the strings in it must be freed after use.
874 GPtrArray *split_path(const guchar *path)
876 GPtrArray *array;
877 guchar *slash;
879 g_return_val_if_fail(path != NULL, NULL);
881 array = g_ptr_array_new();
883 while (1)
885 while (path[0] == '/')
886 path++;
887 if (path[0] == '\0')
888 break;
890 slash = strchr(path, '/');
891 if (slash)
893 g_ptr_array_add(array, g_strndup(path, slash - path));
894 path = slash + 1;
895 continue;
897 g_ptr_array_add(array, g_strdup(path));
898 break;
901 return array;
904 /* Return the shortest path from 'from' to 'to'.
905 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
907 guchar *get_relative_path(const guchar *from, const guchar *to)
909 GString *path;
910 guchar *retval;
911 GPtrArray *src, *dst;
912 int i, j;
914 src = split_path(from);
915 dst = split_path(to);
917 /* The last component of src doesn't matter... */
918 if (src->len)
920 g_free(src->pdata[src->len - 1]);
921 g_ptr_array_remove_index(src, src->len - 1);
924 /* Strip off common path elements... */
925 i = 0;
926 while (i < src->len && i < dst->len)
928 guchar *a = (guchar *) src->pdata[i];
929 guchar *b = (guchar *) dst->pdata[i];
931 if (strcmp(a, b) != 0)
932 break;
933 i++;
936 /* Go up one dir for each element remaining in src */
937 path = g_string_new(NULL);
938 for (j = i; j < src->len; j++)
939 g_string_append(path, "../");
941 /* Go down one dir for each element remaining in dst */
942 for (j = i; j < dst->len; j++)
944 g_string_append(path, (guchar *) dst->pdata[j]);
945 g_string_append_c(path, '/');
948 if (path->str[path->len - 1] == '/')
949 g_string_truncate(path, path->len - 1);
950 if (path->len == 0)
951 g_string_assign(path, ".");
953 /* Free the arrays */
954 for (i = 0; i < src->len; i++)
955 g_free(src->pdata[i]);
956 g_ptr_array_free(src, TRUE);
957 for (i = 0; i < dst->len; i++)
958 g_free(dst->pdata[i]);
959 g_ptr_array_free(dst, TRUE);
961 retval = path->str;
962 g_string_free(path, FALSE);
964 return retval;
968 * Interperet text as a boolean value. Return defvalue if we don't
969 * recognise it
971 int text_to_boolean(const char *text, int defvalue)
973 if(g_strcasecmp(text, "true")==0)
974 return TRUE;
975 else if(g_strcasecmp(text, "false")==0)
976 return FALSE;
977 else if(g_strcasecmp(text, "yes")==0)
978 return TRUE;
979 else if(g_strcasecmp(text, "no")==0)
980 return FALSE;
981 else if(isdigit(text[0]))
982 return !!atoi(text);
984 return defvalue;
987 /* Return the pathname that this symlink points to.
988 * NULL on error (not a symlink, path too long) and errno set.
989 * g_free() the result.
991 char *readlink_dup(const char *source)
993 char path[MAXPATHLEN + 1];
994 int got;
996 got = readlink(source, path, MAXPATHLEN);
997 if (got < 0 || got > MAXPATHLEN)
998 return NULL;
1000 return g_strndup(path, got);
1004 * This code implements the MD5 message-digest algorithm.
1005 * The algorithm is due to Ron Rivest. The original code was
1006 * written by Colin Plumb in 1993, and put in the public domain.
1008 * Modified to use glib datatypes. Put under GPL to simplify
1009 * licensing for ROX-Filer. Taken from Debian's dpkg package.
1012 #define md5byte unsigned char
1014 typedef struct _MD5Context MD5Context;
1016 struct _MD5Context {
1017 guint32 buf[4];
1018 guint32 bytes[2];
1019 guint32 in[16];
1022 #if G_BYTE_ORDER == G_BIG_ENDIAN
1023 void byteSwap(guint32 *buf, unsigned words)
1025 md5byte *p = (md5byte *)buf;
1027 do {
1028 *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
1029 ((unsigned)p[1] << 8 | p[0]);
1030 p += 4;
1031 } while (--words);
1033 #else
1034 #define byteSwap(buf,words)
1035 #endif
1038 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
1039 * initialization constants.
1041 static void MD5Init(MD5Context *ctx)
1043 ctx->buf[0] = 0x67452301;
1044 ctx->buf[1] = 0xefcdab89;
1045 ctx->buf[2] = 0x98badcfe;
1046 ctx->buf[3] = 0x10325476;
1048 ctx->bytes[0] = 0;
1049 ctx->bytes[1] = 0;
1053 * Update context to reflect the concatenation of another buffer full
1054 * of bytes.
1056 static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
1058 guint32 t;
1060 /* Update byte count */
1062 t = ctx->bytes[0];
1063 if ((ctx->bytes[0] = t + len) < t)
1064 ctx->bytes[1]++; /* Carry from low to high */
1066 t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
1067 if (t > len) {
1068 memcpy((md5byte *)ctx->in + 64 - t, buf, len);
1069 return;
1071 /* First chunk is an odd size */
1072 memcpy((md5byte *)ctx->in + 64 - t, buf, t);
1073 byteSwap(ctx->in, 16);
1074 MD5Transform(ctx->buf, ctx->in);
1075 buf += t;
1076 len -= t;
1078 /* Process data in 64-byte chunks */
1079 while (len >= 64) {
1080 memcpy(ctx->in, buf, 64);
1081 byteSwap(ctx->in, 16);
1082 MD5Transform(ctx->buf, ctx->in);
1083 buf += 64;
1084 len -= 64;
1087 /* Handle any remaining bytes of data. */
1088 memcpy(ctx->in, buf, len);
1092 * Final wrapup - pad to 64-byte boundary with the bit pattern
1093 * 1 0* (64-bit count of bits processed, MSB-first)
1094 * Returns the newly allocated string of the hash.
1096 static char *MD5Final(MD5Context *ctx)
1098 char *retval;
1099 int i;
1100 int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
1101 md5byte *p = (md5byte *)ctx->in + count;
1102 guint8 *bytes;
1104 /* Set the first char of padding to 0x80. There is always room. */
1105 *p++ = 0x80;
1107 /* Bytes of padding needed to make 56 bytes (-8..55) */
1108 count = 56 - 1 - count;
1110 if (count < 0) { /* Padding forces an extra block */
1111 memset(p, 0, count + 8);
1112 byteSwap(ctx->in, 16);
1113 MD5Transform(ctx->buf, ctx->in);
1114 p = (md5byte *)ctx->in;
1115 count = 56;
1117 memset(p, 0, count);
1118 byteSwap(ctx->in, 14);
1120 /* Append length in bits and transform */
1121 ctx->in[14] = ctx->bytes[0] << 3;
1122 ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
1123 MD5Transform(ctx->buf, ctx->in);
1125 byteSwap(ctx->buf, 4);
1127 retval = g_malloc(33);
1128 bytes = (guint8 *) ctx->buf;
1129 for (i = 0; i < 16; i++)
1130 sprintf(retval + (i * 2), "%02x", bytes[i]);
1131 retval[32] = '\0';
1133 return retval;
1136 # ifndef ASM_MD5
1138 /* The four core functions - F1 is optimized somewhat */
1140 /* #define F1(x, y, z) (x & y | ~x & z) */
1141 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1142 #define F2(x, y, z) F1(z, x, y)
1143 #define F3(x, y, z) (x ^ y ^ z)
1144 #define F4(x, y, z) (y ^ (x | ~z))
1146 /* This is the central step in the MD5 algorithm. */
1147 #define MD5STEP(f,w,x,y,z,in,s) \
1148 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
1151 * The core of the MD5 algorithm, this alters an existing MD5 hash to
1152 * reflect the addition of 16 longwords of new data. MD5Update blocks
1153 * the data and converts bytes into longwords for this routine.
1155 static void MD5Transform(guint32 buf[4], guint32 const in[16])
1157 register guint32 a, b, c, d;
1159 a = buf[0];
1160 b = buf[1];
1161 c = buf[2];
1162 d = buf[3];
1164 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
1165 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
1166 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
1167 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
1168 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
1169 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
1170 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
1171 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
1172 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
1173 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
1174 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
1175 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
1176 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
1177 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
1178 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
1179 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
1181 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
1182 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
1183 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
1184 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
1185 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
1186 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
1187 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
1188 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
1189 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
1190 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
1191 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
1192 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
1193 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
1194 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1195 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1196 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1198 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1199 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1200 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1201 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1202 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1203 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1204 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1205 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1206 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1207 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1208 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1209 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1210 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1211 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1212 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1213 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1215 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1216 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1217 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1218 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1219 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1220 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1221 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1222 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1223 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1224 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1225 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1226 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1227 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1228 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1229 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1230 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1232 buf[0] += a;
1233 buf[1] += b;
1234 buf[2] += c;
1235 buf[3] += d;
1238 # endif /* ASM_MD5 */
1240 char *md5_hash(const char *message)
1242 MD5Context ctx;
1244 MD5Init(&ctx);
1245 MD5Update(&ctx, message, strlen(message));
1246 return MD5Final(&ctx);
1249 /* Convert string 'src' from the current locale to UTF-8 */
1250 gchar *to_utf8(const gchar *src)
1252 gchar *retval;
1254 if (!src)
1255 return NULL;
1257 retval = g_locale_to_utf8(src, -1, NULL, NULL, NULL);
1259 return retval ? retval : g_strdup(src);
1262 /* Convert string 'src' to the current locale from UTF-8 */
1263 gchar *from_utf8(const gchar *src)
1265 gchar *retval;
1267 if (!src)
1268 return NULL;
1270 retval = g_locale_from_utf8(src, -1, NULL, NULL, NULL);
1272 return retval ? retval : g_strdup(src);