1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
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 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18 
19 #include "crashhandlerform.h"
20 #include "messagebox.h"
21 #include "pgmodeleruins.h"
22 
23 const QString CrashHandlerForm::AnalysisMode("-analysis-mode");
24 
CrashHandlerForm(bool analysis_mode,QWidget * parent,Qt::WindowFlags f)25 CrashHandlerForm::CrashHandlerForm(bool analysis_mode, QWidget *parent, Qt::WindowFlags f) : BugReportForm(parent, f)
26 {
27 	QFile input;
28 	QString buf;
29 	QWidget *wgt=new QWidget;
30 	QHBoxLayout *layout=new QHBoxLayout;
31 
32 	setWindowTitle(tr("pgModeler crash handler"));
33 
34 	stack_txt=new QPlainTextEdit(this);
35 	stack_txt->setReadOnly(true);
36 	stack_txt->setFont(QFont("Source Code Pro"));
37 	stack_txt->setLineWrapMode(QPlainTextEdit::NoWrap);
38 
39 	layout->addWidget(stack_txt);
40 	layout->setContentsMargins(4,4,4,4);
41 	wgt->setLayout(layout);
42 
43 	logo_lbl->setPixmap(QPixmap(QString(":/imagens/imagens/crashhandler.png")));
44 	report_twg->addTab(wgt, tr("Stack trace"));
45 
46 	//Open for reading the stack trace file generated on the last crash
47 	input.setFileName(GlobalAttributes::getTemporaryFilePath(GlobalAttributes::StacktraceFile));
48 	input.open(QFile::ReadOnly);
49 
50 	if(input.isOpen())
51 	{
52 		buf=input.readAll();
53 		input.close();
54 
55 		//Removes the stack trace file
56 		QDir stack_file;
57 		stack_file.remove(GlobalAttributes::getTemporaryFilePath(GlobalAttributes::StacktraceFile));
58 
59 		//Shows the stacktrace loaded on the widget
60 		stack_txt->setPlainText(buf);
61 	}
62 
63 	//Creating an input field in order to select the input report file
64 	input_wgt=new QWidget(this);
65 	input_wgt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
66 
67 	layout=new QHBoxLayout(input_wgt);
68 	layout->setContentsMargins(0, 0, 0, 0);
69 
70 	input_lbl=new QLabel(input_wgt);
71 	input_lbl->setText(tr("Input:"));
72 	layout->addWidget(input_lbl);
73 
74 	input_sel = new FileSelectorWidget(this);
75 	input_sel->setFileDialogTitle(tr("Select bug report file"));
76 	input_sel->setFileMode(QFileDialog::ExistingFile);
77 	input_sel->setAcceptMode(QFileDialog::AcceptOpen);
78 	input_sel->setNameFilters({ tr("pgModeler bug report (*.bug)"), tr("All files (*.*)") });
79 	input_sel->setToolTip(tr("Load report file for analysis"));
80 	layout->addWidget(input_sel);
81 
82 	save_tb=new QToolButton(input_wgt);
83 	save_tb->setIcon(QPixmap(PgModelerUiNs::getIconPath("salvar")));
84 	save_tb->setSizePolicy(attach_tb->sizePolicy());
85 	save_tb->setToolButtonStyle(attach_tb->toolButtonStyle());
86 	save_tb->setIconSize(attach_tb->iconSize());
87 	save_tb->setToolTip(tr("Save the attached model file on the filesystem"));
88 	save_tb->setEnabled(false);
89 	attach_wgt->layout()->addWidget(save_tb);
90 
91 	report_tab_grid->removeWidget(details_gb);
92 	report_tab_grid->removeWidget(output_wgt);
93 	report_tab_grid->removeWidget(message_frm);
94 
95 	report_tab_grid->addWidget(input_wgt);
96 	report_tab_grid->addWidget(details_gb);
97 	report_tab_grid->addWidget(output_wgt);
98 	report_tab_grid->addWidget(message_frm);
99 
100 	setAnalysisMode(analysis_mode);
101 
102 	connect(input_sel, SIGNAL(s_fileSelected(QString)), this, SLOT(loadReport(QString)));
103 	connect(input_sel, SIGNAL(s_selectorCleared()), model_txt, SLOT(clear()));
104 	connect(input_sel, SIGNAL(s_selectorCleared()), details_txt, SLOT(clear()));
105 	connect(input_sel, SIGNAL(s_selectorCleared()), stack_txt, SLOT(clear()));
106 	connect(save_tb, SIGNAL(clicked()), this, SLOT(saveModel()));
107 
108 	connect(model_txt, &QPlainTextEdit::textChanged, [&](){
109 			save_tb->setEnabled(!model_txt->toPlainText().isEmpty());
110 	});
111 }
112 
loadReport(QString filename)113 void CrashHandlerForm::loadReport(QString filename)
114 {
115 	QFile input;
116 	QFileInfo fi;
117 	char *buf=nullptr;
118 	Messagebox msgbox;
119 
120 	fi.setFile(filename);
121 	input.setFileName(filename);
122 	input.open(QFile::ReadOnly);
123 
124 	//Raises an error if the file could not be opened
125 	if(!input.isOpen())
126 		msgbox.show(Exception::getErrorMessage(ErrorCode::FileDirectoryNotAccessed).arg(filename), Messagebox::ErrorIcon);
127 	else
128 	{
129 		QByteArray uncomp_buf;
130 		QString buf_aux, str_aux;
131 		int i, idx;
132 		QPlainTextEdit *txt_widgets[]={ details_txt, model_txt , stack_txt};
133 
134 		//Creates a text buffer
135 		buf=new char[fi.size()];
136 
137 		//Reads the file storing it on the buffer
138 		input.read(buf, fi.size());
139 		input.close();
140 
141 		//Uncompress the buffer
142 		uncomp_buf.append(buf, fi.size());
143 		uncomp_buf=qUncompress(uncomp_buf);
144 
145 		delete[](buf);
146 		buf=nullptr;
147 
148 		buf_aux=QString(uncomp_buf.data());
149 		i=idx=0;
150 
151 		//Showing the sections of the uncompressed buffer on the respective widgets
152 		while(i < buf_aux.size() && idx <= 2)
153 		{
154 			if(buf_aux.at(i).toLatin1()!=CharDelimiter)
155 				str_aux.append(buf_aux.at(i));
156 			else
157 			{
158 				txt_widgets[idx++]->setPlainText(str_aux);
159 				str_aux.clear();
160 			}
161 			i++;
162 		}
163 	}
164 }
165 
saveModel()166 void CrashHandlerForm::saveModel()
167 {
168 	QFileDialog file_dlg;
169 
170 	try
171 	{
172 		file_dlg.setDefaultSuffix(QString("dbm"));
173 		file_dlg.setWindowTitle(tr("Save model"));
174 		file_dlg.setNameFilter(tr("Database model (*.dbm);;All files (*.*)"));
175 		file_dlg.setFileMode(QFileDialog::AnyFile);
176 		file_dlg.setAcceptMode(QFileDialog::AcceptSave);
177 		file_dlg.setModal(true);
178 
179 		if(file_dlg.exec()==QFileDialog::Accepted)
180 		{
181 			QFile output(file_dlg.selectedFiles().at(0));
182 			QByteArray buf;
183 
184 			output.open(QFile::WriteOnly);
185 
186 			if(!output.isOpen())
187 				throw Exception(Exception::getErrorMessage(ErrorCode::FileDirectoryNotWritten).arg(file_dlg.selectedFiles().at(0)),
188 												ErrorCode::FileDirectoryNotWritten,__PRETTY_FUNCTION__,__FILE__,__LINE__);
189 
190 			buf.append(model_txt->toPlainText());
191 			output.write(buf.data(),buf.size());
192 			output.close();
193 		}
194 	}
195 	catch(Exception &e)
196 	{
197 		Messagebox msgbox;
198 		msgbox.show(e);
199 	}
200 }
201 
setAnalysisMode(bool value)202 void CrashHandlerForm::setAnalysisMode(bool value)
203 {
204 	output_wgt->setEnabled(!value);
205 	attach_tb->setEnabled(!value);
206 	attach_mod_chk->setEnabled(!value);
207 	save_tb->setVisible(value);
208 
209 	create_btn->setVisible(!value);
210 	input_wgt->setVisible(value);
211 
212 	if(value)
213 	{
214 		title_lbl->setText(tr("pgModeler crash handler"));
215 		msg_lbl->setText(tr("Bug report analysis mode activated."));
216 	}
217 	else
218 	{
219 		title_lbl->setText(tr("Oops! pgModeler just crashed!"));
220 		msg_lbl->setText(tr("We apologize for what happened! It is clear that a nasty bug caused that. Please fill out the form below describing your actions before pgModeler quit unexpectedly. This will help on bug extermination and improve the software."));
221 	}
222 }
223 
generateReportBuffer()224 QByteArray CrashHandlerForm::generateReportBuffer()
225 {
226 	QByteArray buf=BugReportForm::generateReportBuffer();
227 	buf.append(stack_txt->toPlainText().toUtf8());
228 	buf.append(CharDelimiter);
229 
230 	return buf;
231 }
232