about summary refs log tree commit diff stats
path: root/src/tinyurl.c
blob: 666cf9a16b277d2be648a3ba776f24836d961539 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* 
 * tinyurl.c
 *
 * Copyright (C) 2012 James Booth <boothj5@gmail.com>
 * 
 * This file is part of Profanity.
 *
 * Profanity 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 3 of the License, or
 * (at your option) any later version.
 *
 * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdlib.h>
#include <string.h>

#include <curl/curl.h>
#include <curl/easy.h>
#include <glib.h>

struct curl_data_t
{
    char *buffer;
    size_t size;
};

static size_t _data_callback(void *ptr, size_t size, size_t nmemb, void *data);

gboolean
tinyurl_valid(char *url)
{
    return (g_str_has_prefix(url, "http://") || 
        g_str_has_prefix(url, "https://"));
}

char *
tinyurl_get(char *url)
{
    GString *full_url = g_string_new("http://tinyurl.com/api-create.php?url=");
    g_string_append(full_url, url);

    CURL *handle = curl_easy_init();
    CURLcode result; 
    struct curl_data_t output; 
    output.buffer = NULL;
    output.size = 0;

    curl_easy_setopt(handle, CURLOPT_URL, full_url->str);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, _data_callback);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&output);
    
    result = curl_easy_perform(handle);
    curl_easy_cleanup(handle);

    output.buffer[output.size++] = '\0';
    g_string_free(full_url, TRUE);

    return output.buffer;
}

static size_t 
_data_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
    size_t realsize = size * nmemb;
    struct curl_data_t *mem = (struct curl_data_t *) data;
    mem->buffer = realloc(mem->buffer, mem->size + realsize + 1);

    if ( mem->buffer )
    {
        memcpy( &( mem->buffer[ mem->size ] ), ptr, realsize );
        mem->size += realsize;
        mem->buffer[ mem->size ] = 0;
    }
    
    return realsize;
}
="nx">criteria.WithoutFlags, maildir.FlagSeen) case 'x': criteria.WithFlags = append(criteria.WithFlags, getParsedFlag(opt.Value)) case 'X': criteria.WithoutFlags = append(criteria.WithoutFlags, getParsedFlag(opt.Value)) case 'H': // TODO case 'f': criteria.Header.Add("From", opt.Value) case 't': criteria.Header.Add("To", opt.Value) case 'c': criteria.Header.Add("Cc", opt.Value) case 'b': body = true case 'a': text = true } } if text { criteria.Text = args[optind:] } else if body { criteria.Body = args[optind:] } else { for _, arg := range args[optind:] { criteria.Header.Add("Subject", arg) } } return criteria, nil } func getParsedFlag(name string) maildir.Flag { var f maildir.Flag switch strings.ToLower(name) { case "seen": f = maildir.FlagSeen case "answered": f = maildir.FlagReplied case "flagged": f = maildir.FlagFlagged } return f } func (w *Worker) search(criteria *searchCriteria) ([]uint32, error) { requiredParts := getRequiredParts(criteria) w.worker.Logger.Printf("Required parts bitmask for search: %b", requiredParts) keys, err := w.c.UIDs(*w.selected) if err != nil { return nil, err } matchedUids := []uint32{} for _, key := range keys { success, err := w.searchKey(key, criteria, requiredParts) if err != nil { // don't return early so that we can still get some results w.worker.Logger.Printf("Failed to search key %v: %v", key, err) } else if success { matchedUids = append(matchedUids, key) } } return matchedUids, nil } // Execute the search criteria for the given key, returns true if search succeeded func (w *Worker) searchKey(key uint32, criteria *searchCriteria, parts MsgParts) (bool, error) { message, err := w.c.Message(*w.selected, key) if err != nil { return false, err } // setup parts of the message to use in the search // this is so that we try to minimise reading unnecessary parts var ( flags []maildir.Flag header *models.MessageInfo body string all string ) if parts&FLAGS > 0 { flags, err = message.Flags() if err != nil { return false, err } } if parts&HEADER > 0 { header, err = message.MessageInfo() if err != nil { return false, err } } if parts&BODY > 0 { // TODO: select which part to search, maybe look for text/plain mi, err := message.MessageInfo() if err != nil { return false, err } path := lib.FindFirstNonMultipart(mi.BodyStructure, nil) reader, err := message.NewBodyPartReader(path) if err != nil { return false, err } bytes, err := ioutil.ReadAll(reader) if err != nil { return false, err } body = string(bytes) } if parts&ALL > 0 { reader, err := message.NewReader() if err != nil { return false, err } bytes, err := ioutil.ReadAll(reader) if err != nil { return false, err } all = string(bytes) } // now search through the criteria // implicit AND at the moment so fail fast if criteria.Header != nil { for k, v := range criteria.Header { headerValue := header.RFC822Headers.Get(k) for _, text := range v { if !containsSmartCase(headerValue, text) { return false, nil } } } } if criteria.Body != nil { for _, searchTerm := range criteria.Body { if !containsSmartCase(body, searchTerm) { return false, nil } } } if criteria.Text != nil { for _, searchTerm := range criteria.Text { if !containsSmartCase(all, searchTerm) { return false, nil } } } if criteria.WithFlags != nil { for _, searchFlag := range criteria.WithFlags { if !containsFlag(flags, searchFlag) { return false, nil } } } if criteria.WithoutFlags != nil { for _, searchFlag := range criteria.WithoutFlags { if containsFlag(flags, searchFlag) { return false, nil } } } return true, nil } // Returns true if searchFlag appears in flags func containsFlag(flags []maildir.Flag, searchFlag maildir.Flag) bool { match := false for _, flag := range flags { if searchFlag == flag { match = true } } return match } // Smarter version of strings.Contains for searching. // Is case-insensitive unless substr contains an upper case character func containsSmartCase(s string, substr string) bool { if hasUpper(substr) { return strings.Contains(s, substr) } return strings.Contains(strings.ToLower(s), strings.ToLower(substr)) } func hasUpper(s string) bool { for _, r := range s { if unicode.IsUpper(r) { return true } } return false } // The parts of a message, kind of type MsgParts int const NONE MsgParts = 0 const ( FLAGS MsgParts = 1 << iota HEADER BODY ALL ) // Returns a bitmask of the parts of the message required to be loaded for the // given criteria func getRequiredParts(criteria *searchCriteria) MsgParts { required := NONE if len(criteria.Header) > 0 { required |= HEADER } if criteria.Body != nil && len(criteria.Body) > 0 { required |= BODY } if criteria.Text != nil && len(criteria.Text) > 0 { required |= ALL } if criteria.WithFlags != nil && len(criteria.WithFlags) > 0 { required |= FLAGS } if criteria.WithoutFlags != nil && len(criteria.WithoutFlags) > 0 { required |= FLAGS } return required }