├── README.org ├── metaweblog-pkg.el ├── metaweblog.el └── test-metaweblog.el /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: MetaWeblog 2 | 3 | [[https://github.com/org2blog/org2blog/blob/master/metaweblog.el][MetaWeblog]] is now stored in the [[https://github.com/org2blog/org2blog][Org2Blog repo.]] 4 | -------------------------------------------------------------------------------- /metaweblog-pkg.el: -------------------------------------------------------------------------------- 1 | (define-package "metaweblog" "1.0.1" 2 | "An emacs library to access metaweblog based weblogs" 3 | '((xml-rpc "1.6.8"))) 4 | -------------------------------------------------------------------------------- /metaweblog.el: -------------------------------------------------------------------------------- 1 | ;;; metaweblog.el --- an emacs library to access metaweblog based weblogs 2 | ;; Copyright (C) 2008 Ashish Shukla 3 | ;; Copyright (C) 2010 Puneeth Chaganti 4 | 5 | ;; This program is free software: you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | 18 | (require 'xml-rpc) 19 | 20 | (defconst metaweblog-version "1.0.1" 21 | "Current version of metaweblog.el") 22 | 23 | (defun metaweblog-get-categories (blog-xmlrpc user-name password blog-id) 24 | "Retrieves list of categories from the weblog system" 25 | (xml-rpc-method-call blog-xmlrpc 26 | "metaWeblog.getCategories" 27 | blog-id 28 | user-name 29 | password)) 30 | 31 | (defun wp-new-category (blog-xmlrpc user-name password blog-id category) 32 | "Create new category on the weblog" 33 | (xml-rpc-method-call blog-xmlrpc 34 | "wp.newCategory" 35 | blog-id 36 | user-name 37 | password 38 | `(("name" . ,category)))) 39 | 40 | (defun wp-get-tags (blog-xmlrpc user-name password blog-id) 41 | "Retrieves list of tags from the weblog system. Uses wp.getTags" 42 | (xml-rpc-method-call blog-xmlrpc 43 | "wp.getTags" 44 | blog-id 45 | user-name 46 | password)) 47 | 48 | (defun wp-get-pages (blog-xmlrpc user-name password blog-id) 49 | "Retrieves list of pages from the weblog system. Uses wp.getPages." 50 | (xml-rpc-method-call blog-xmlrpc 51 | "wp.getPages" 52 | blog-id 53 | user-name 54 | password)) 55 | 56 | (defun wp-get-pagelist (blog-xmlrpc user-name password blog-id) 57 | "Retrieves list of pages (minimal information) from the weblog 58 | system. Uses wp.getPageList." 59 | (xml-rpc-method-call blog-xmlrpc 60 | "wp.getPageList" 61 | blog-id 62 | user-name 63 | password)) 64 | 65 | (defun metaweblog-new-post 66 | (blog-xmlrpc user-name password blog-id content publish) 67 | "Sends a new post to the blog. If PUBLISH is non-nil, the post is 68 | published, otherwise it is saved as draft. CONTENT will be an alist 69 | title, description, categories, and date as keys (string-ified) mapped to the 70 | title of the post, post contents, list of categories, and date respectively." 71 | (let ((post-title (cdr (assoc "title" content))) 72 | (post-description (cdr (assoc "description" content))) 73 | (post-categories (cdr (assoc "categories" content))) 74 | (post-tags (cdr (assoc "tags" content))) 75 | (post-excerpt (cdr (assoc "excerpt" content))) 76 | (post-permalink (cdr (assoc "permalink" content))) 77 | (post-date (cdr (assoc "date" content)))) 78 | ;;; since xml-rpc-method-call entitifies the HTML text in the post 79 | ;;; we've to use raw 80 | (xml-rpc-xml-to-response (xml-rpc-request 81 | blog-xmlrpc 82 | `((methodCall 83 | nil 84 | (methodName nil "metaWeblog.newPost") 85 | (params nil 86 | (param nil (value nil (string nil ,blog-id))) 87 | (param nil (value nil (string nil ,user-name))) 88 | (param nil (value nil (string nil ,password))) 89 | (param nil (value nil 90 | (struct 91 | nil 92 | (member nil 93 | (name nil "title") 94 | (value nil ,post-title)) 95 | (member nil 96 | (name nil "description") 97 | (value nil ,post-description)) 98 | (member nil 99 | (name nil "mt_excerpt") 100 | (value nil ,post-excerpt)) 101 | (member nil 102 | (name nil "wp_slug") 103 | (value nil ,post-permalink)) 104 | (member nil 105 | (name nil "dateCreated") 106 | (dateTime.iso8601 nil ,post-date)) 107 | ,(when post-tags 108 | `(member nil 109 | (name nil "mt_keywords") 110 | (value nil 111 | (array 112 | nil 113 | ,(append 114 | '(data nil) 115 | (mapcar 116 | (lambda(f) 117 | `(value nil (string nil ,f))) 118 | post-tags)))))) 119 | ,(when post-categories 120 | `(member nil 121 | (name nil "categories") 122 | (value nil 123 | (array 124 | nil 125 | ,(append 126 | '(data nil) 127 | (mapcar 128 | (lambda(f) 129 | `(value nil (string nil ,f))) 130 | post-categories))))))))) 131 | (param nil (value nil (boolean nil ,(if publish "1" "0"))))))))))) 132 | 133 | (defun wp-new-page 134 | (blog-xmlrpc user-name password blog-id content publish) 135 | "Sends a new page to the blog. If PUBLISH is non-nil, the post is 136 | published, otherwise it is saved as draft. CONTENT will be an alist 137 | title, description, categories, and date as keys (string-ified) mapped to the 138 | title of the post, post contents, list of categories, and date respectively." 139 | (let ((post-title (cdr (assoc "title" content))) 140 | (post-description (cdr (assoc "description" content))) 141 | (post-categories (cdr (assoc "categories" content))) 142 | (post-tags (cdr (assoc "tags" content))) 143 | (post-excerpt (cdr (assoc "excerpt" content))) 144 | (post-permalink (cdr (assoc "permalink" content))) 145 | (post-parent (cdr (assoc "parent" content))) 146 | (post-date (cdr (assoc "date" content)))) 147 | ;;; since xml-rpc-method-call entitifies the HTML text in the post 148 | ;;; we've to use raw 149 | (xml-rpc-xml-to-response (xml-rpc-request 150 | blog-xmlrpc 151 | `((methodCall 152 | nil 153 | (methodName nil "wp.newPage") 154 | (params nil 155 | (param nil (value nil (string nil ,blog-id))) 156 | (param nil (value nil (string nil ,user-name))) 157 | (param nil (value nil (string nil ,password))) 158 | (param nil (value nil 159 | (struct 160 | nil 161 | (member nil 162 | (name nil "title") 163 | (value nil ,post-title)) 164 | (member nil 165 | (name nil "description") 166 | (value nil ,post-description)) 167 | (member nil 168 | (name nil "mt_excerpt") 169 | (value nil ,post-excerpt)) 170 | (member nil 171 | (name nil "wp_slug") 172 | (value nil ,post-permalink)) 173 | (member nil 174 | (name nil "wp_page_parent_id") 175 | (value nil ,post-parent)) 176 | (member nil 177 | (name nil "dateCreated") 178 | (dateTime.iso8601 nil ,post-date)) 179 | ,(when post-tags 180 | `(member nil 181 | (name nil "mt_keywords") 182 | (value nil 183 | (array 184 | nil 185 | ,(append 186 | '(data nil) 187 | (mapcar 188 | (lambda(f) 189 | `(value nil (string nil ,f))) 190 | post-tags)))))) 191 | ,(when post-categories 192 | `(member nil 193 | (name nil "categories") 194 | (value nil 195 | (array 196 | nil 197 | ,(append 198 | '(data nil) 199 | (mapcar 200 | (lambda(f) 201 | `(value nil (string nil ,f))) 202 | post-categories))))))))) 203 | (param nil (value nil (boolean nil ,(if publish "1" "0"))))))))))) 204 | 205 | (defun wp-edit-page 206 | (blog-xmlrpc user-name password blog-id post-id content publish) 207 | "Edits an existing page on the blog. If PUBLISH is non-nil, the 208 | post is published, otherwise it is saved as draft. CONTENT will 209 | be an alist title, description, categories, and date as 210 | keys (string-ified) mapped to the title of the post, post 211 | contents, list of categories, and date respectively." 212 | (let ((post-title (cdr (assoc "title" content))) 213 | (post-description (cdr (assoc "description" content))) 214 | (post-categories (cdr (assoc "categories" content))) 215 | (post-tags (cdr (assoc "tags" content))) 216 | (post-excerpt (cdr (assoc "excerpt" content))) 217 | (post-permalink (cdr (assoc "permalink" content))) 218 | (post-parent (cdr (assoc "parent" content))) 219 | (post-date (cdr (assoc "date" content)))) 220 | (message post-date) 221 | ;;; since xml-rpc-method-call entitifies the HTML text in the post 222 | ;;; we've to use raw 223 | (xml-rpc-xml-to-response 224 | (xml-rpc-request 225 | blog-xmlrpc 226 | `((methodCall 227 | nil 228 | (methodName nil "wp.editPage") 229 | (params nil 230 | (param nil (value nil (string nil ,blog-id))) 231 | (param nil (value nil (string nil ,post-id))) 232 | (param nil (value nil (string nil ,user-name))) 233 | (param nil (value nil (string nil ,password))) 234 | (param nil (value nil 235 | (struct 236 | nil 237 | (member nil 238 | (name nil "title") 239 | (value nil ,post-title)) 240 | (member nil 241 | (name nil "description") 242 | (value nil ,post-description)) 243 | (member nil 244 | (name nil "mt_excerpt") 245 | (value nil ,post-excerpt)) 246 | (member nil 247 | (name nil "wp_slug") 248 | (value nil ,post-permalink)) 249 | (member nil 250 | (name nil "wp_page_parent_id") 251 | (value nil ,post-parent)) 252 | (member nil 253 | (name nil "dateCreated") 254 | (dateTime.iso8601 nil ,post-date)) 255 | ,(when post-tags 256 | `(member nil 257 | (name nil "mt_keywords") 258 | (value nil 259 | (array 260 | nil 261 | ,(append 262 | '(data nil) 263 | (mapcar 264 | (lambda(f) 265 | `(value nil (string nil ,f))) 266 | post-tags)))))) 267 | ,(when post-categories 268 | `(member nil 269 | (name nil "categories") 270 | (value nil 271 | (array 272 | nil 273 | ,(append 274 | '(data nil) 275 | (mapcar 276 | (lambda(f) 277 | `(value nil (string nil ,f))) 278 | post-categories))))))))) 279 | (param nil (value nil (boolean nil ,(if publish "1" "0"))))))))))) 280 | 281 | (defun metaweblog-edit-post 282 | (blog-xmlrpc user-name password post-id content publish) 283 | "Edits an exiting post, if post-id is given. If PUBLISH is non-nil, the 284 | post is published, otherwise it is saved as draft. CONTENT will be an alist 285 | title, description, categories, and date as keys (string-ified) mapped to the 286 | title of the post, post contents, list of categories, and date respectively." 287 | (let ((post-title (cdr (assoc "title" content))) 288 | (post-description (cdr (assoc "description" content))) 289 | (post-categories (cdr (assoc "categories" content))) 290 | (post-tags (cdr (assoc "tags" content))) 291 | (post-excerpt (cdr (assoc "excerpt" content))) 292 | (post-permalink (cdr (assoc "permalink" content))) 293 | (post-date (cdr (assoc "date" content)))) 294 | (message post-date) 295 | ;;; since xml-rpc-method-call entitifies the HTML text in the post 296 | ;;; we've to use raw 297 | (xml-rpc-xml-to-response (xml-rpc-request 298 | blog-xmlrpc 299 | `((methodCall 300 | nil 301 | (methodName nil "metaWeblog.editPost") 302 | (params nil 303 | (param nil (value nil (string nil ,post-id))) 304 | (param nil (value nil (string nil ,user-name))) 305 | (param nil (value nil (string nil ,password))) 306 | (param nil (value nil 307 | (struct 308 | nil 309 | (member nil 310 | (name nil "title") 311 | (value nil ,post-title)) 312 | (member nil 313 | (name nil "description") 314 | (value nil ,post-description)) 315 | (member nil 316 | (name nil "mt_excerpt") 317 | (value nil ,post-excerpt)) 318 | (member nil 319 | (name nil "wp_slug") 320 | (value nil ,post-permalink)) 321 | (member nil 322 | (name nil "dateCreated") 323 | (dateTime.iso8601 nil ,post-date)) 324 | ,(when post-tags 325 | `(member nil 326 | (name nil "mt_keywords") 327 | (value nil 328 | (array 329 | nil 330 | ,(append 331 | '(data nil) 332 | (mapcar 333 | (lambda(f) 334 | `(value nil (string nil ,f))) 335 | post-tags)))))) 336 | ,(when post-categories 337 | `(member nil 338 | (name nil "categories") 339 | (value nil 340 | (array 341 | nil 342 | ,(append 343 | '(data nil) 344 | (mapcar 345 | (lambda(f) 346 | `(value nil (string nil ,f))) 347 | post-categories))))))))) 348 | (param nil (value nil (boolean nil ,(if publish "1" "0"))))))))))) 349 | 350 | (defun metaweblog-get-post (blog-xmlrpc user-name password post-id) 351 | "Retrieves a post from the weblog. POST-ID is the id of the post 352 | which is to be returned. Can be used with pages as well." 353 | (xml-rpc-method-call blog-xmlrpc 354 | "metaWeblog.getPost" 355 | post-id 356 | user-name 357 | password)) 358 | 359 | (defun metaweblog-delete-post (blog-xmlrpc user-name password post-id) 360 | "Delete an entry from the weblog system." 361 | (xml-rpc-method-call blog-xmlrpc 362 | "blogger.deletePost" 363 | nil 364 | post-id 365 | user-name 366 | password 367 | t)) 368 | 369 | (defun wp-delete-page (blog-xmlrpc blog-id user-name password page-id) 370 | "Delete a page from the weblog system." 371 | (xml-rpc-method-call blog-xmlrpc 372 | "wp.deletePage" 373 | blog-id 374 | user-name 375 | password 376 | page-id)) 377 | 378 | (defun metaweblog-get-recent-posts(blog-xmlrpc blog-id user-name password number-of-posts) 379 | "Fetches the recent posts from the weblog. NUMBER-OF-POSTS is the 380 | no. of posts that should be returned." 381 | (xml-rpc-method-call blog-xmlrpc 382 | "metaWeblog.getRecentPosts" 383 | blog-id 384 | user-name 385 | password 386 | number-of-posts)) 387 | 388 | (defun get-file-properties (file) 389 | "Gets the properties of a file. Returns an assoc list with 390 | name - file name 391 | bits - data of the file as a base64 encoded string 392 | type - mimetype of file deduced from extension." 393 | (let* (base64-str type name) 394 | (save-excursion 395 | (save-restriction 396 | (with-current-buffer (find-file-noselect file nil t) 397 | (fundamental-mode) 398 | (setq name (file-name-nondirectory file)) 399 | (setq base64-str (base64-encode-string (encode-coding-string (buffer-string) 'binary))) 400 | (setq type (mailcap-extension-to-mime (or (file-name-extension file) ""))) 401 | (kill-buffer) 402 | (setq file-props `(("name" . ,name) 403 | ("bits" . ,base64-str) 404 | ("type" . ,type)))))) 405 | file-props)) 406 | 407 | (defun metaweblog-upload-file (blog-xmlrpc user-name password blog-id file) 408 | "Uploads file to the blog. FILE will be an alist name, type, 409 | bits, as keys mapped to name of the file, mime type and the 410 | data." 411 | (let ((file-name (cdr (assoc "name" file))) 412 | (file-type (cdr (assoc "type" file))) 413 | (file-bits (cdr (assoc "bits" file)))) 414 | 415 | (xml-rpc-xml-to-response 416 | (xml-rpc-request 417 | blog-xmlrpc 418 | `((methodCall 419 | nil 420 | (methodName nil "metaWeblog.newMediaObject") 421 | (params nil 422 | (param nil (value nil (string nil ,blog-id))) 423 | (param nil (value nil (string nil ,user-name))) 424 | (param nil (value nil (string nil ,password))) 425 | (param nil (value nil 426 | (struct 427 | nil 428 | (member nil 429 | (name nil "name") 430 | (value nil ,file-name)) 431 | (member nil 432 | (name nil "bits") 433 | (base64 nil ,file-bits)) 434 | (member nil 435 | (name nil "type") 436 | (value nil ,file-type)) 437 | (member nil 438 | (name nil "overwrite") 439 | (value nil "t"))))) 440 | ))))))) 441 | 442 | 443 | (provide 'metaweblog) 444 | -------------------------------------------------------------------------------- /test-metaweblog.el: -------------------------------------------------------------------------------- 1 | ;;; test-metaweblog.el --- tests for metaweblog.el 2 | ;; Copyright (C) 2012 Puneeth Chaganti 3 | 4 | (require 'metaweblog) 5 | 6 | ;;;;; Test env-setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (setq blog-xmlrpc "http://localhost/xmlrpc.php" 9 | blog-user "admin" 10 | blog-pass "test123" 11 | blog-id "1") 12 | 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | ; Util functions 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | (defun random-string (length) 18 | "Return a random string of given length" 19 | (let ((alpha "abcdefghijklmnopqrstuvwxyz") 20 | (char-list)) 21 | (dotimes (char length) 22 | (setq char-list (cons (string (elt "abcdefghijklmnopqrstuvwxyz" (random 25))) char-list))) 23 | (mapconcat 'identity char-list ""))) 24 | 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | ; Tests 27 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 28 | 29 | (ert-deftest test-new-category () 30 | "Test if creating a new category works." 31 | (let* ((category (random-string 8)) 32 | ;; Add new category 33 | (category-id (wp-new-category blog-xmlrpc blog-user blog-pass blog-id category))) 34 | ;; List all categories and check if our category is present that list 35 | (setq categories (metaweblog-get-categories blog-xmlrpc blog-user blog-pass blog-id)) 36 | (dolist (cat categories) 37 | (if (equal (cdr (assoc "categoryId" cat)) category-id) 38 | (should (equal (cdr (assoc "categoryName" cat)) category)))))) 39 | 40 | (ert-deftest test-new-tag () 41 | "Test if getting tags works. This test does nothing, just 42 | checks if Wordpress version has the API. 43 | FIXME: Make this a real test ..." 44 | (let* () 45 | (setq tag-list (wp-get-tags blog-xmlrpc blog-user blog-pass blog-id)))) 46 | 47 | (ert-deftest test-pages () 48 | "Test if creating, listing, fetching content and deleting pages works." 49 | (let* ((content '(("date" . "20120817T18:30:00+0000") 50 | ("title" . "Hello World") 51 | ("tags" "org2blog" "emacs") 52 | ("categories" "org2blog" "emacs") 53 | ("post-id") 54 | ("parent" . "0") 55 | ("excerpt" . "") 56 | ("permalink" . "") 57 | ("description" . "

Test content.

"))) 58 | 59 | ;; Post a new page 60 | (page-id (wp-new-page blog-xmlrpc blog-user blog-pass blog-id content nil)) 61 | (fetched-page (metaweblog-get-post blog-xmlrpc blog-user blog-pass page-id)) 62 | (new-content "

New Content

")) 63 | 64 | ;; Check if fetched-page's content and posted content match 65 | (should (equal (cdr (assoc "description" fetched-page)) (cdr (assoc "description" content)))) 66 | 67 | ;; Change content and post again... 68 | (setcdr (assoc "description" content) new-content) 69 | (wp-edit-page blog-xmlrpc blog-user blog-pass blog-id page-id content nil) 70 | ;; Fetch page again and check if content changed 71 | (setq fetched-page (metaweblog-get-post blog-xmlrpc blog-user blog-pass page-id)) 72 | (should (equal (cdr (assoc "description" fetched-page)) (cdr (assoc "description" content)))) 73 | 74 | ;; List pages 75 | ;; Check if our page is listed in minimal listing 76 | (dolist (page (wp-get-pagelist blog-xmlrpc blog-user blog-pass blog-id)) 77 | (if (equal (cdr (assoc "post_id" page)) page-id) 78 | (should (equal (cdr (assoc "page_title" page)) (cdr (assoc "title" content)))))) 79 | 80 | ;; Check if our page is listed in full listing 81 | (dolist (page (wp-get-pages blog-xmlrpc blog-user blog-pass blog-id)) 82 | (if (equal (cdr (assoc "post_id" page)) page-id) 83 | (should (equal (cdr (assoc "page_title" page)) (cdr (assoc "title" content)))))) 84 | 85 | ;; Delete page 86 | (wp-delete-page blog-xmlrpc blog-id blog-user blog-pass page-id))) 87 | 88 | (ert-deftest test-posts () 89 | "Test if creating, listing, fetching content and deleting posts works." 90 | (let* ((content '(("date" . "20120817T18:30:00+0000") 91 | ("title" . "Hello World") 92 | ("tags" "org2blog" "emacs") 93 | ("categories" "org2blog" "emacs") 94 | ("post-id") 95 | ("parent" . "0") 96 | ("excerpt" . "") 97 | ("permalink" . "") 98 | ("description" . "

Test content.

"))) 99 | 100 | ;; Make a new post 101 | (post-id (metaweblog-new-post blog-xmlrpc blog-user blog-pass blog-id content nil)) 102 | (fetched-post (metaweblog-get-post blog-xmlrpc blog-user blog-pass post-id)) 103 | (new-content "

New Content

")) 104 | 105 | ;; Check if fetched-post's content and posted content match 106 | (should (equal (cdr (assoc "description" fetched-post)) (cdr (assoc "description" content)))) 107 | 108 | ;; Change content and post again... 109 | (setcdr (assoc "description" content) new-content) 110 | (metaweblog-edit-post blog-xmlrpc blog-user blog-pass post-id content nil) 111 | ;; Fetch post again and check if content changed 112 | (setq fetched-post (metaweblog-get-post blog-xmlrpc blog-user blog-pass post-id)) 113 | (should (equal (cdr (assoc "description" fetched-post)) (cdr (assoc "description" content)))) 114 | 115 | ;; List recent posts and check if our post is listed 116 | (dolist (post (metaweblog-get-recent-posts blog-xmlrpc blog-id blog-user blog-pass 1000)) 117 | (when (equal (cdr (assoc "postid" post)) post-id) 118 | (should (equal (cdr (assoc "title" post)) (cdr (assoc "title" content)))) 119 | (should (equal (cdr (assoc "description" post)) (cdr (assoc "description" content)))))) 120 | 121 | ;; Delete post 122 | (metaweblog-delete-post blog-xmlrpc blog-user blog-pass post-id))) 123 | 124 | 125 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 | ;; FIXME: Write tests for these functions ... 127 | ;; (defun get-file-properties (file) 128 | ;; "Gets the properties of a file. Returns an assoc list with 129 | ;; name - file name 130 | ;; bits - data of the file as a base64 encoded string 131 | ;; type - mimetype of file deduced from extension.") 132 | 133 | ;; (defun metaweblog-upload-file (blog-xmlrpc user-name password blog-id file) 134 | ;; "Uploads file to the blog. FILE will be an alist name, type, 135 | ;; bits, as keys mapped to name of the file, mime type and the 136 | ;; data.") 137 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 138 | --------------------------------------------------------------------------------