summaryrefslogtreecommitdiffstats
path: root/lib/Plugins/Catcut.cpp
blob: 6bb44a9d35e29e7ee05ff38f2f57b0463665de5a (plain)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>

#include "abrtlib.h"
#include "Catcut.h"
#include "CrashTypes.h"
#include "DebugDump.h"
#include "ABRTException.h"
#include "CommLayerInner.h"
#ifdef HAVE_CONFIG_H
    #include "config.h"
#endif

using namespace std;

static xmlrpc_env env;
static xmlrpc_client* client = NULL;
static struct xmlrpc_clientparms clientParms;
static struct xmlrpc_curl_xportparms curlParms;
static xmlrpc_server_info* server_info = NULL;


static string login(const char* login, const char* passwd);
//static void logout();
static void new_xmlrpc_client(const char* url, bool no_ssl_verify);
static void destroy_xmlrpc_client();
static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription);
static void get_product_and_version(const string& pRelease,
                                          string& pProduct,
                                          string& pVersion);


static void throw_if_xml_fault_occurred()
{
    if (env.fault_occurred)
    {
        string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
        error_msg("%s", errmsg.c_str()); // show error in daemon log
        throw CABRTException(EXCEP_PLUGIN, errmsg);
    }
}

static void new_xmlrpc_client(const char* url, bool no_ssl_verify)
{
    xmlrpc_env_init(&env);

    /* This should be done at program startup, once.
     * We do it in abrtd's main */
    /* xmlrpc_client_setup_global_const(&env); */

    curlParms.network_interface = NULL;
    curlParms.no_ssl_verifypeer = no_ssl_verify;
    curlParms.no_ssl_verifyhost = no_ssl_verify;
#ifdef VERSION
    curlParms.user_agent        = PACKAGE_NAME"/"VERSION;
#else
    curlParms.user_agent        = "abrt";
#endif

    clientParms.transport          = "curl";
    clientParms.transportparmsP    = &curlParms;
    clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent);

    xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE_NAME, VERSION, &clientParms, XMLRPC_CPSIZE(transportparm_size),
                         &client);
    throw_if_xml_fault_occurred();

    server_info = xmlrpc_server_info_new(&env, url);
    throw_if_xml_fault_occurred();
}

static void destroy_xmlrpc_client()
{
    xmlrpc_server_info_free(server_info);
    xmlrpc_env_clean(&env);
    xmlrpc_client_destroy(client);
}

static string login(const char* login, const char* passwd)
{
    xmlrpc_value* param = xmlrpc_build_value(&env, "(ss)", login, passwd);
    throw_if_xml_fault_occurred();

    xmlrpc_value* result;
    xmlrpc_client_call2(&env, client, server_info, "Catcut.auth", param, &result);
    throw_if_xml_fault_occurred();
    xmlrpc_DECREF(param);

    xmlrpc_value *cookie_xml;
    const char *cookie;
    string cookie_str;
    xmlrpc_struct_find_value(&env, result, "cookie", &cookie_xml);
    throw_if_xml_fault_occurred();
    xmlrpc_read_string(&env, cookie_xml, &cookie);
    throw_if_xml_fault_occurred();
    cookie_str = cookie;
    /* xmlrpc_read_string returns *malloc'ed ptr*.
     * doc is not very clear on it, but I looked in xmlrpc sources. */
    free((void*)cookie);
    xmlrpc_DECREF(cookie_xml);

    xmlrpc_DECREF(result);

    return cookie_str;
}

// catcut does not have it (yet?)
//static void logout()
//{
//    xmlrpc_value* param = xmlrpc_build_value(&env, "(s)", "");
//    throw_if_xml_fault_occurred();
//
//    xmlrpc_value* result = NULL; /* paranoia */
//    xmlrpc_client_call2(&env, client, server_info, "User.logout", param, &result);
//    throw_if_xml_fault_occurred();
//}

static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription)
{
    string howToReproduce;
    string comment;

    if (pCrashReport.find(CD_REPRODUCE) != pCrashReport.end())
    {
        howToReproduce = "\n\nHow to reproduce\n"
                         "-----\n" +
                         pCrashReport.find(CD_REPRODUCE)->second[CD_CONTENT];
    }
    if (pCrashReport.find(CD_COMMENT) != pCrashReport.end())
    {
        comment = "\n\nComment\n"
                 "-----\n" +
                 pCrashReport.find(CD_COMMENT)->second[CD_CONTENT];
    }
    pDescription = "\nabrt detected a crash.\n" +
                   howToReproduce +
                   comment +
                   "\n\nAdditional information\n"
                   "======\n";

    map_crash_report_t::const_iterator it;
    for (it = pCrashReport.begin(); it != pCrashReport.end(); it++)
    {
        if (it->second[CD_TYPE] == CD_TXT)
        {
            if (it->first != CD_UUID &&
                it->first != FILENAME_ARCHITECTURE &&
                it->first != FILENAME_RELEASE &&
                it->first != CD_REPRODUCE &&
                it->first != CD_COMMENT)
            {
                pDescription += "\n" + it->first + "\n";
                pDescription += "-----\n";
                pDescription += it->second[CD_CONTENT] + "\n\n";
            }
        }
        else if (it->second[CD_TYPE] == CD_ATT)
        {
            pDescription += "\n\nAttached files\n"
                            "----\n";
            pDescription += it->first + "\n";
        }
        else if (it->second[CD_TYPE] == CD_BIN)
        {
            string msg = ssprintf(_("Binary file %s will not be reported."), it->first.c_str());
            warn_client(msg);
            //update_client(_("Binary file ")+it->first+_(" will not be reported."));
        }
    }
}

static void get_product_and_version(const string& pRelease,
                                          string& pProduct,
                                          string& pVersion)
{
    if (pRelease.find("Rawhide") != string::npos)
    {
        pProduct = "Fedora";
        pVersion = "rawhide";
        return;
    }
    if (pRelease.find("Fedora") != string::npos)
    {
        pProduct = "Fedora";
    }
    else if (pRelease.find("Red Hat Enterprise Linux") != string::npos)
    {
        pProduct = "Red Hat Enterprise Linux ";
    }
    string::size_type pos = pRelease.find("release");
    pos = pRelease.find(" ", pos) + 1;
    while (pRelease[pos] != ' ')
    {
        pVersion += pRelease[pos];
        if (pProduct == "Red Hat Enterprise Linux ")
        {
            pProduct += pRelease[pos];
        }
        pos++;
    }
}

static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport)
{
    string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT];
    string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT];
    string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT];
    string arch = pCrashReport.find(FILENAME_ARCHITECTURE)->second[CD_CONTENT];
    string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT];

    string summary = "[abrt] crash detected in " + package;
    string status_whiteboard = "abrt_hash:" + uuid;

    string description;
    create_new_bug_description(pCrashReport, description);

    string product;
    string version;
    get_product_and_version(release, product, version);

    xmlrpc_value *param = xmlrpc_build_value(&env, "(s{s:s,s:s,s:s,s:s,s:s,s:s,s:s})",
                auth_cookie,
                "product", product.c_str(),
                "component", component.c_str(),
                "version", version.c_str(),
                "summary", summary.c_str(),
                "description", description.c_str(),
                "status_whiteboard", status_whiteboard.c_str(),
                "platform", arch.c_str()
                );
    throw_if_xml_fault_occurred();

    xmlrpc_value *result;
    xmlrpc_client_call2(&env, client, server_info, "Catcut.createTicket", param, &result);
    throw_if_xml_fault_occurred();
    xmlrpc_DECREF(param);

    xmlrpc_value *bug_id_xml;
    const char *bug_id;
    string bug_id_str;
    xmlrpc_struct_find_value(&env, result, "ticket", &bug_id_xml);
    throw_if_xml_fault_occurred();
    xmlrpc_read_string(&env, bug_id_xml, &bug_id);
    throw_if_xml_fault_occurred();
    bug_id_str = bug_id;
    log("New bug id: %s", bug_id);
    update_client(_("New bug id: ") + bug_id_str);
    free((void*)bug_id);
    xmlrpc_DECREF(bug_id_xml);

    xmlrpc_DECREF(result);

    return bug_id_str;
}

//static
//void add_attachments(const string& pBugId, const map_crash_report_t& pCrashReport)
//{
//    xmlrpc_value* result = NULL;
//
//    map_crash_report_t::const_iterator it = pCrashReport.begin();
//    for (; it != pCrashReport.end(); it++)
//    {
//        if (it->second[CD_TYPE] == CD_ATT)
//        {
//            string description = "File: " + it->first;
//            const string& to_encode = it->second[CD_CONTENT];
//            char *encoded64 = encode_base64(to_encode.c_str(), to_encode.length());
//            xmlrpc_value* param = xmlrpc_build_value(&env,"(s{s:s,s:s,s:s,s:s})",
//                                              pBugId.c_str(),
//                                              "description", description.c_str(),
//                                              "filename", it->first.c_str(),
//                                              "contenttype", "text/plain",
//                                              "data", encoded64
//                                      );
//            free(encoded64);
//            throw_if_xml_fault_occurred();
//
//// catcut has this API:
//// struct response requestUpload(string cookie, string ticket, string filename, string description)
////response MUST include "errno", "errmsg" members; if an upload is approved,
////a "URL" MUST be returned in the response. The description string
////should include a brief description of the file.
////
////The client should upload the file via HTTP PUT to the provided
////URL.  The provided URL may be absolute or relative, if relative it must
////be combined with the base URL of the XML-RPC server using the usual
////rules for relative URL's (RFC 3986).
//            xmlrpc_client_call2(&env, client, server_info, "catcut.addAttachment", param, &result);
//            throw_if_xml_fault_occurred();
//        }
//    }
//}

CReporterCatcut::CReporterCatcut() :
    m_sCatcutURL("http://127.0.0.1:8080/catcut/xmlrpc"),
    m_bNoSSLVerify(false)
{}

CReporterCatcut::~CReporterCatcut()
{}

string CReporterCatcut::Report(const map_crash_report_t& pCrashReport,
                               const map_plugin_settings_t& pSettings, const string& pArgs)
{
    update_client(_("Creating new bug..."));
    try
    {
        new_xmlrpc_client(m_sCatcutURL.c_str(), m_bNoSSLVerify);
        string auth_cookie = login(m_sLogin.c_str(), m_sPassword.c_str());
        string bug_id = (auth_cookie != "") ? new_bug(auth_cookie.c_str(), pCrashReport) : "";
//        add_attachments(to_string(bug_id), pCrashReport);
//        update_client(_("Logging out..."));
//        logout();
        destroy_xmlrpc_client();
        return "New catcut bug ID: " + bug_id;

    }
    catch (CABRTException& e)
    {
        destroy_xmlrpc_client();
        throw CABRTException(EXCEP_PLUGIN, string("CReporterCatcut::Report(): ") + e.what());
    }
}

void CReporterCatcut::SetSettings(const map_plugin_settings_t& pSettings)
{
    map_plugin_settings_t::const_iterator it;
    map_plugin_settings_t::const_iterator end = pSettings.end();

    it = pSettings.find("CatcutURL");
    if (it != end)
    {
        m_sCatcutURL = it->second;
    }
    it = pSettings.find("Login");
    if (it != end)
    {
        m_sLogin = it->second;
    }
    it = pSettings.find("Password");
    if (it != end)
    {
        m_sPassword = it->second;
    }
    it = pSettings.find("NoSSLVerify");
    if (it != end)
    {
        m_bNoSSLVerify = it->second == "yes";
    }
}

map_plugin_settings_t CReporterCatcut::GetSettings()
{
    map_plugin_settings_t ret;

    ret["CatcutURL"] = m_sCatcutURL;
    ret["Login"] = m_sLogin;
    ret["Password"] = m_sPassword;
    ret["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no";

    return ret;
}

PLUGIN_INFO(REPORTER,
            CReporterCatcut,
            "Catcut",
            "0.0.1",
            "Test plugin to report bugs to catcut and if not, report it.",
            "dvlasenk@redhat.com",
            "https://fedorahosted.org/abrt/wiki",
            PLUGINS_LIB_DIR"/Catcut.GTKBuilder");