'''
bug_report.py

Copyright 2009 Andres Riancho

This file is part of w3af, w3af.sourceforge.net .

w3af 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 version 2 of the License.

w3af 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 w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

'''

import sys
import gtk

# w3af crash File creation
from core.controllers.easy_contribution.sourceforge import sourceforge
import cgi
import time
import webbrowser
from core.ui.gtkUi.helpers import endThreads


class simple_base_window(gtk.Window):
    def __init__(self, type=None):
        '''
        One simple class to create other windows.
        '''
        super(simple_base_window,self).__init__(type=gtk.WINDOW_TOPLEVEL)
        self.connect("delete-event", gtk.main_quit)
        
    def _handle_cancel(self, widg):
        # Exit w3af
        endThreads()
        self.destroy()
        #gtk.main_quit()
        #sys.exit(-1)
        
class bug_report_result(simple_base_window):
    def __init__(self, text, url):
        '''
        A class that shows the result of a bug report to the user.
        '''
        super(bug_report_result,self).__init__(type=gtk.WINDOW_TOPLEVEL)
        
        # Save for later
        self.url = url
        self.text = text
        
        self.set_modal(True)
        self.set_title('Bug report results')
        self.set_icon_from_file('core/ui/gtkUi/data/w3af_icon.png')
        self.vbox = gtk.VBox()
        
        self.label = gtk.Label()
        self.label.set_line_wrap(True)
        self.label.set_selectable(True)
        
        # The label text
        self.label.set_markup( text )
        
        self.label.show()
        self.vbox.pack_start(self.label, True, False)
        
        # The link
        link = gtk.EventBox()
        link.set_events(gtk.gdk.BUTTON_PRESS_MASK)
        link_label_text = '\n<u><span foreground="#0000AA">'
        link_label_text += cgi.escape(url)+'</span></u>\n'
        linkLabel = gtk.Label( link_label_text )
        linkLabel.set_line_wrap(True)
        linkLabel.set_use_markup( True )
        link.add( linkLabel )
        link.connect( 'button_press_event', self.on_link_clicked )
        link.show_all()
        self.vbox.pack_start(link)
        
        # Close button
        self.butt_close = gtk.Button(stock=gtk.STOCK_CLOSE)
        self.butt_close.connect("clicked", self._handle_cancel )
        self.vbox.pack_start(self.butt_close, True, False)
        
        self.add(self.vbox)
        self.show_all()
        
        # This is a quick fix to get around the problem generated by "set_selectable"
        # that selects the text by default
        self.label.select_region(0, 0)
      
    def on_link_clicked(self, widg, evt):
        '''
        Open a web browser with the corresponding URL, sometimes it's the
        URL for the newly reported bug, sometimes it's the URL for a manual bug report.
        '''
        webbrowser.open( self.url )

class bug_report_window(simple_base_window):
    def __init__(self, title, exception_text, w3af_version, filename):
        '''
        The first window that the user sees when a bug was detected.
        '''
        super(bug_report_window,self).__init__(type=gtk.WINDOW_TOPLEVEL)
        
        # Set generic window settings
        self.set_modal(True)
        self.set_title(title)
        self.set_icon_from_file('core/ui/gtkUi/data/w3af_icon.png')
        self.connect("delete-event", gtk.main_quit)
        self.vbox = gtk.VBox()
        
        # Internal variables
        self.bug_url = None
        self.manual_bug_report = 'https://sourceforge.net/apps/trac/w3af/newticket'
        
        # Save the info for later
        self.exception_text = exception_text
        self.w3af_version = w3af_version
        self.filename = filename
        
        # the label for the title
        self.title_label = gtk.Label()
        self.title_label.set_line_wrap(True)
        label_text = _('\n<b>An unhandled exception was raised:</b>\n\n')
        self.title_label.set_markup( label_text )
        self.title_label.show()
        
        # A gtk.TextView for the exception
        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        
        self.text_view = gtk.TextView()
        self.text_view.set_size_request(150, 250) 
        self.text_view.set_editable(False)
        self.text_view.set_wrap_mode( gtk.WRAP_CHAR )

        buffer = self.text_view.get_buffer()
        buffer.set_text(exception_text)
        
        sw.add(self.text_view)
        
        # the label for the rest of the message
        self.label = gtk.Label()
        self.label.set_line_wrap(True)
        label_text = _("<i>All this info is in a file called ") + filename + _(" for later review.</i>\n\n")
        label_text += _('If you wish, <b>you can contribute</b> to the w3af project and submit this')
        label_text += _(' bug to the sourceforge\'s Trac system from within this window.')
        label_text += _(' It\'s a simple <i>two step process</i>.\n\n')
        label_text += _('w3af will only send the exception traceback and the version information to')
        label_text += _(' sourceforge, no personal or confidencial information is collected.\n\n')
        label_text += _('Please click "Ok" to contribute, or "Cancel" to go back to w3af (possibly with an invalid state).\n')
        self.label.set_markup( label_text )
        self.label.show()
        
        self.vbox.pack_start(self.title_label, True, False)
        self.vbox.pack_start(sw, False, False)
        self.vbox.pack_start(self.label, True, False)
        
        # the buttons
        self.hbox = gtk.HBox()
        self.butt_send = gtk.Button(stock=gtk.STOCK_OK)
        self.butt_send.connect("clicked", self._handle_send )
        self.hbox.pack_start(self.butt_send, True, False)
        
        self.butt_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
        self.butt_cancel.connect("clicked", self._handle_cancel )
        self.hbox.pack_start(self.butt_cancel, True, False)
        self.vbox.pack_start(self.hbox, True, False)
        
        # Init sourceforge object
        self.sourceforge = sourceforge()
        
        #self.resize(400,450)
        self.add(self.vbox)
        self.show_all()
        
        # This is a quick fix to get around the problem generated by "set_selectable"
        # that selects the text by default
        self.label.select_region(0, 0)

    def _ask_bug_information(self):
        '''
        @return: A tuple with the bug title and the bug description provided by the user.
        '''
        default_text = '''What steps will reproduce the problem?
1. 
2. 
3. 

What is the expected output? What do you see instead?


What operating system are you using?


Please provide any additional information below:


'''
        #base this on a message dialog
        dialog = gtk.MessageDialog(
            None,
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_QUESTION,
            gtk.BUTTONS_OK,
            None)
        dialog.set_icon_from_file('core/ui/gtkUi/data/w3af_icon.png')
        
        msg = '<b>Step 1 of 2</b>\n\n\n'
        msg += 'Please provide the following information about the bug:\n'
        dialog.set_markup( msg )
        
        #create the text input field
        summary_entry = gtk.Entry()
        
        sw = gtk.ScrolledWindow()
        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        description_text_view = gtk.TextView()
        buffer = description_text_view.get_buffer()
        buffer.set_text(default_text)
        sw.add(description_text_view)
        
        #create a horizontal box to pack the entry and a label
        summary_hbox = gtk.HBox()
        summary_hbox.pack_start(gtk.Label("Summary:  "), False, 5, 5)
        summary_hbox.pack_end(summary_entry)
        
        description_hbox = gtk.HBox()
        description_hbox.pack_start(gtk.Label("Description:"), False, 5, 5)
        description_hbox.pack_start(sw, True, True, 0)
        
        #add it and show it
        dialog.vbox.pack_start(summary_hbox, True, True, 0)
        dialog.vbox.pack_start(description_hbox, True, True, 0)
        dialog.show_all()
        
        #go go go
        dialog.run()
        summary = summary_entry.get_text()
        description = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
        dialog.destroy()
        
        return summary, description
        

    def _handle_send(self, widg):
        '''
        Handle the Ok button click. This is the "main" of this window.
        '''
        # Ask for a bug title and description
        summary, description = self._ask_bug_information()
        
        invalid_login = False
        while True:
            # Ask for user and password, or anonymous
            user, password = self._ask_user_password( invalid_login )
        
            if user == password == '':
                # anonymous bug report, no login.
                login_ok = True
            else:
                # Login to sourceforge
                login_ok = self.sourceforge.login( user, password )
                invalid_login = True
            
            if login_ok:
                break
        
        self.bug_url = self.sourceforge.report_bug(summary, description,
                                                        self.w3af_version, self.exception_text,
                                                        self.filename)
                                                        
        if self.bug_url:
            # Show the tracking URL to the user
            text = _('<b>Thanks for reporting your bugs!</b>\n\n')
            text += _('You can track your bug report here:\n')
            brf = bug_report_result(text, self.bug_url)
        else:
            # bug report failed
            label_text = _('<b>The bug report failed!</b>\n\n')
            label_text += _('Please try to report this bug manually submitting the information')
            label_text += _(' contained inside the <i>'+self.filename+'</i> file here: ')
            brf = bug_report_result(label_text, self.manual_bug_report)
            
        brf.show()
            
    def _ask_user_password(self, invalid_login=False):
        '''
        @return: A tuple with the user and the password to use. Blank in both if anonymous.
        '''
        #base this on a message dialog
        dialog = gtk.MessageDialog(
            None,
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_QUESTION,
            gtk.BUTTONS_OK,
            None)
        dialog.set_icon_from_file('core/ui/gtkUi/data/w3af_icon.png')
        
        
        msg = '<b>Step 2 of 2</b>\n\n\n'
        if invalid_login:
            msg += '<b><i>Your credentials are invalid, please try again.</i></b>\n\n'
            
        msg += 'Please enter your <b>sourceforge credentials</b> or leave both'
        msg += ' text entries <i>blank if you want to report the bug <b>anonymously</b></i>.'
        dialog.set_markup( msg )
        
        #create the text input field
        user_entry = gtk.Entry()
        user_entry.connect("activate", lambda x: dialog.response(gtk.RESPONSE_OK))  
        passwd_entry = gtk.Entry()
        passwd_entry.connect("activate", lambda x: dialog.response(gtk.RESPONSE_OK) )
        passwd_entry.set_visibility(False)
        
        #create a horizontal box to pack the entry and a label
        user_hbox = gtk.HBox()
        user_hbox.pack_start(gtk.Label("Username:"), False, 5, 5)
        user_hbox.pack_end(user_entry)
        
        passwd_hbox = gtk.HBox()
        passwd_hbox.pack_start(gtk.Label("Password:  "), False, 5, 5)
        passwd_hbox.pack_end(passwd_entry)
        
        #some secondary text
        warning = "Your credentials won't be stored in your computer, and"
        warning += " will only be sent over secure HTTPS connections."
        dialog.format_secondary_markup( warning )
        
        #add it and show it
        dialog.vbox.pack_start(user_hbox, True, True, 0)
        dialog.vbox.pack_start(passwd_hbox, True, True, 0)
        dialog.show_all()
        
        #go go go
        dialog.run()
        user = user_entry.get_text()
        passwd = passwd_entry.get_text()
        dialog.destroy()
        
        return user, passwd
