aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/oscar/info.h
blob: b4d99e9fcba039526412dc3d47f94d8407defe27 (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
#ifndef __OSCAR_INFO_H__
#define __OSCAR_INFO_H__

#define AIM_CB_FAM_LOC 0x0002

/*
 * SNAC Family: Location Services.
 */ 
#define AIM_CB_LOC_ERROR 0x0001
#define AIM_CB_LOC_REQRIGHTS 0x0002
#define AIM_CB_LOC_RIGHTSINFO 0x0003
#define AIM_CB_LOC_SETUSERINFO 0x0004
#define AIM_CB_LOC_REQUSERINFO 0x0005
#define AIM_CB_LOC_USERINFO 0x0006
#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
#define AIM_CB_LOC_WATCHERNOT 0x0008
#define AIM_CB_LOC_DEFAULT 0xffff

#define AIM_CAPS_BUDDYICON      0x00000001
#define AIM_CAPS_VOICE          0x00000002
#define AIM_CAPS_IMIMAGE        0x00000004
#define AIM_CAPS_CHAT           0x00000008
#define AIM_CAPS_GETFILE        0x00000010
#define AIM_CAPS_SENDFILE       0x00000020
#define AIM_CAPS_GAMES          0x00000040
#define AIM_CAPS_SAVESTOCKS     0x00000080
#define AIM_CAPS_SENDBUDDYLIST  0x00000100
#define AIM_CAPS_GAMES2         0x00000200
#define AIM_CAPS_ICQ            0x00000400
#define AIM_CAPS_APINFO         0x00000800
#define AIM_CAPS_ICQRTF	        0x00001000
#define AIM_CAPS_EMPTY	        0x00002000
#define AIM_CAPS_ICQSERVERRELAY 0x00004000
#define AIM_CAPS_ICQUNKNOWN     0x00008000
#define AIM_CAPS_TRILLIANCRYPT  0x00010000
#define AIM_CAPS_UTF8           0x00020000
#define AIM_CAPS_INTEROP        0x00040000
#define AIM_CAPS_ICHAT          0x00080000
#define AIM_CAPS_EXTCHAN2       0x00100000
#define AIM_CAPS_LAST           0x00200000

int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn);

#endif /* __OSCAR_INFO_H__ */
> 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
package FixMyStreet::Script::Reports;

use strict;
use warnings;

use CronFns;
use DateTime::Format::Pg;

use Utils;
use Utils::OpenStreetMap;

use FixMyStreet;
use FixMyStreet::Cobrand;
use FixMyStreet::DB;
use FixMyStreet::Email;
use FixMyStreet::Map;
use FixMyStreet::SendReport;

sub send(;$) {
    my ($site_override) = @_;
    my $rs = FixMyStreet::DB->resultset('Problem');

    # Set up site, language etc.
    my ($verbose, $nomail, $debug_mode) = CronFns::options();
    my $test_data;

    my $base_url = FixMyStreet->config('BASE_URL');
    my $site = $site_override || CronFns::site($base_url);

    my $states = [ FixMyStreet::DB::Result::Problem::open_states() ];
    $states = [ 'submitted', 'confirmed', 'in progress', 'feedback pending', 'external', 'wish' ] if $site eq 'zurich';
    my $unsent = $rs->search( {
        state => $states,
        whensent => undef,
        bodies_str => { '!=', undef },
    } );
    my (%notgot, %note);

    my $send_report = FixMyStreet::SendReport->new();
    my $senders = $send_report->get_senders;

    my $debug_unsent_count = 0;
    debug_print("starting to loop through unsent problem reports...") if $debug_mode;
    while (my $row = $unsent->next) {

        my $cobrand = $row->get_cobrand_logged;
        FixMyStreet::DB->schema->cobrand($cobrand);

        if ($debug_mode) {
            $debug_unsent_count++;
            print "\n";
            debug_print("state=" . $row->state . ", bodies_str=" . $row->bodies_str . ($row->cobrand? ", cobrand=" . $row->cobrand : ""), $row->id);
        }

        # Cobranded and non-cobranded messages can share a database. In this case, the conf file
        # should specify a vhost to send the reports for each cobrand, so that they don't get sent
        # more than once if there are multiple vhosts running off the same database. The email_host
        # call checks if this is the host that sends mail for this cobrand.
        if (! $cobrand->email_host()) {
            debug_print("skipping because this host does not send reports for cobrand " . $cobrand->moniker, $row->id) if $debug_mode;
            next;
        }

        $cobrand->set_lang_and_domain($row->lang, 1);
        FixMyStreet::Map::set_map_class($cobrand->map_type);
        if ( $row->is_from_abuser) {
            $row->update( { state => 'hidden' } );
            debug_print("hiding because its sender is flagged as an abuser", $row->id) if $debug_mode;
            next;
        } elsif ( $row->title =~ /app store test/i ) {
            $row->update( { state => 'hidden' } );
            debug_print("hiding because it is an app store test message", $row->id) if $debug_mode;
            next;
        }

        # Template variables for the email
        my $email_base_url = $cobrand->base_url_for_report($row);
        my %h = map { $_ => $row->$_ } qw/id title detail name category latitude longitude used_map/;
        $h{report} = $row;
        $h{cobrand} = $cobrand;
        map { $h{$_} = $row->user->$_ || '' } qw/email phone/;
        $h{confirmed} = DateTime::Format::Pg->format_datetime( $row->confirmed->truncate (to => 'second' ) )
            if $row->confirmed;

        $h{query} = $row->postcode;
        $h{url} = $email_base_url . $row->url;
        $h{admin_url} = $row->admin_url($cobrand);
        if ($row->photo) {
            $h{has_photo} = _("This web page also contains a photo of the problem, provided by the user.") . "\n\n";
            $h{image_url} = $email_base_url . $row->photos->[0]->{url_full};
            my @all_images = map { $email_base_url . $_->{url_full} } @{ $row->photos };
            $h{all_image_urls} = \@all_images;
        } else {
            $h{has_photo} = '';
            $h{image_url} = '';
        }
        $h{fuzzy} = $row->used_map ? _('To view a map of the precise location of this issue')
            : _('The user could not locate the problem on a map, but to see the area around the location they entered');
        $h{closest_address} = '';

        $h{osm_url} = Utils::OpenStreetMap::short_url($h{latitude}, $h{longitude});
        if ( $row->used_map ) {
            $h{closest_address} = $cobrand->find_closest($row);
            $h{osm_url} .= '?m';
        }

        if ( $cobrand->allow_anonymous_reports &&
             $row->user->email eq $cobrand->anonymous_account->{'email'}
         ) {
            $h{anonymous_report} = 1;
        }

        $cobrand->call_hook(process_additional_metadata_for_email => $row, \%h);

        my $bodies = FixMyStreet::DB->resultset('Body')->search(
            { id => $row->bodies_str_ids },
            { order_by => 'name' },
        );

        my $missing;
        if ($row->bodies_missing) {
            my @missing = FixMyStreet::DB->resultset("Body")->search(
                { id => [ split /,/, $row->bodies_missing ] },
                { order_by => 'name' }
            )->get_column('name')->all;
            $missing = join(' / ', @missing) if @missing;
        }

        my $send_confirmation_email = $cobrand->report_sent_confirmation_email;

        my @dear;
        my %reporters = ();
        my $skip = 0;
        while (my $body = $bodies->next) {
            # See if this body wants confirmation email (in case report made on national site, for example)
            if (my $cobrand_body = $body->get_cobrand_handler) {
                if (my $id_ref = $cobrand_body->report_sent_confirmation_email) {
                    $send_confirmation_email = $id_ref;
                }
            }

            my $sender_info = $cobrand->get_body_sender( $body, $row->category );
            my $sender = "FixMyStreet::SendReport::" . $sender_info->{method};

            if ( ! exists $senders->{ $sender } ) {
                warn sprintf "No such sender [ $sender ] for body %s ( %d )", $body->name, $body->id;
                next;
            }
            $reporters{ $sender } ||= $sender->new();

            my $inspection_required = $sender_info->{contact}
                ? $sender_info->{contact}->get_extra_metadata('inspection_required')
                : undef;
            if ( $inspection_required ) {
                my $reputation_threshold = $sender_info->{contact}->get_extra_metadata('reputation_threshold') || 0;
                my $reputation_threshold_met = 0;
                if ( $reputation_threshold > 0 ) {
                    my $user_reputation = $row->user->get_extra_metadata('reputation') || 0;
                    $reputation_threshold_met = $user_reputation >= $reputation_threshold;
                }
                unless (
                        $row->get_extra_metadata('inspected') ||
                        $row->user->has_permission_to( trusted => $row->bodies_str_ids ) ||
                        $reputation_threshold_met
                ) {
                    $skip = 1;
                    debug_print("skipped because not yet inspected", $row->id) if $debug_mode;
                }
            }

            if ( $reporters{ $sender }->should_skip( $row, $debug_mode ) ) {
                $skip = 1;
                debug_print("skipped by sender " . $sender_info->{method} . " (might be due to previous failed attempts?)", $row->id) if $debug_mode;
            } else {
                debug_print("OK, adding recipient body " . $body->id . ":" . $body->name . ", " . $sender_info->{method}, $row->id) if $debug_mode;
                push @dear, $body->name;
                $reporters{ $sender }->add_body( $body, $sender_info->{config} );
            }

            # If we are in the UK include eastings and northings
            if ( $cobrand->country eq 'GB' && !$h{easting} ) {
                ( $h{easting}, $h{northing}, $h{coordsyst} ) = $row->local_coords;
            }
        }

        unless ( keys %reporters ) {
            die 'Report not going anywhere for ID ' . $row->id . '!';
        }

        next if $skip;

        if ($h{category} eq _('Other')) {
            $h{category_footer} = _('this type of local problem');
        } else {
            $h{category_footer} = "'" . $h{category} . "'";
        }

        $h{bodies_name} = join(_(' and '), @dear);
        if ($h{category} eq _('Other')) {
            $h{multiple} = @dear>1 ? "[ " . _("This email has been sent to both councils covering the location of the problem, as the user did not categorise it; please ignore it if you're not the correct council to deal with the issue, or let us know what category of problem this is so we can add it to our system.") . " ]\n\n"
                : '';
        } else {
            $h{multiple} = @dear>1 ? "[ " . _("This email has been sent to several councils covering the location of the problem, as the category selected is provided for all of them; please ignore it if you're not the correct council to deal with the issue.") . " ]\n\n"
                : '';
        }
        $h{missing} = '';
        if ($missing) {
            $h{missing} = '[ '
              . sprintf(_('We realise this problem might be the responsibility of %s; however, we don\'t currently have any contact details for them. If you know of an appropriate contact address, please do get in touch.'), $missing)
              . " ]\n\n";
        }

        if (FixMyStreet->staging_flag('send_reports', 0)) {
            # on a staging server send emails to ourselves rather than the bodies
            %reporters = map { $_ => $reporters{$_} } grep { /FixMyStreet::SendReport::Email/ } keys %reporters;
            unless (%reporters) {
                %reporters = ( 'FixMyStreet::SendReport::Email' => FixMyStreet::SendReport::Email->new() );
            }
        }

        # Multiply results together, so one success counts as a success.
        my $result = -1;

        my @methods;
        for my $sender ( keys %reporters ) {
            debug_print("sending using " . $sender, $row->id) if $debug_mode;
            $sender = $reporters{$sender};
            my $res = $sender->send( $row, \%h );
            $result *= $res;
            push @methods, $sender if !$res;
            if ( $sender->unconfirmed_counts) {
                foreach my $e (keys %{ $sender->unconfirmed_counts } ) {
                    foreach my $c (keys %{ $sender->unconfirmed_counts->{$e} }) {
                        $notgot{$e}{$c} += $sender->unconfirmed_counts->{$e}{$c};
                    }
                }
                %note = (%note, %{ $sender->unconfirmed_notes });
            }
            $test_data->{test_req_used} = $sender->open311_test_req_used
                if FixMyStreet->test_mode && $sender->can('open311_test_req_used');
        }

        # Add the send methods now because e.g. Open311
        # send() calls $row->discard_changes
        foreach (@methods) {
            $row->add_send_method($_);
        }

        unless ($result) {
            $row->update( {
                whensent => \'current_timestamp',
                lastupdate => \'current_timestamp',
            } );
            if ($send_confirmation_email && !$h{anonymous_report}) {
                $h{sent_confirm_id_ref} = $row->$send_confirmation_email;
                _send_report_sent_email( $row, \%h, $nomail, $cobrand );
            }
            debug_print("send successful: OK", $row->id) if $debug_mode;
        } else {
            my @errors;
            for my $sender ( keys %reporters ) {
                unless ( $reporters{ $sender }->success ) {
                    push @errors, $reporters{ $sender }->error;
                }
            }
            $row->update_send_failed( join( '|', @errors ) );
            debug_print("send FAILED: " . join( '|', @errors ), $row->id) if $debug_mode;
        }
    }
    if ($debug_mode) {
        print "\n";
        if ($debug_unsent_count) {
            debug_print("processed all unsent reports (total: $debug_unsent_count)");
        } else {
            debug_print("no unsent reports were found (must have whensent=null and suitable bodies_str & state) -- nothing to send");
        }
    }

    if ($verbose || $debug_mode) {
        print "Council email addresses that need checking:\n" if keys %notgot;
        foreach my $e (keys %notgot) {
            foreach my $c (keys %{$notgot{$e}}) {
                print "    " . $notgot{$e}{$c} . " problem, to $e category $c (" . $note{$e}{$c}. ")\n";
            }
        }
        my $sending_errors = '';
        my $unsent = $rs->search( {
            state => [ FixMyStreet::DB::Result::Problem::open_states() ],
            whensent => undef,
            bodies_str => { '!=', undef },
            send_fail_count => { '>', 0 }
        } );
        while (my $row = $unsent->next) {
            my $base_url = FixMyStreet->config('BASE_URL');
            $sending_errors .= "\n" . '=' x 80 . "\n\n" . "* " . $base_url . "/report/" . $row->id . ", failed "
                . $row->send_fail_count . " times, last at " . $row->send_fail_timestamp
                . ", reason " . $row->send_fail_reason . "\n";
        }
        if ($sending_errors) {
            print "The following reports had problems sending:\n$sending_errors";
        }
    }

    return $test_data;
}

sub _send_report_sent_email {
    my $row = shift;
    my $h = shift;
    my $nomail = shift;
    my $cobrand = shift;

    # Don't send 'report sent' text
    return unless $row->user->email_verified;

    my $contributed_as = $row->get_extra_metadata('contributed_as') || '';
    return if $contributed_as eq 'body' || $contributed_as eq 'anonymous_user';

    FixMyStreet::Email::send_cron(
        $row->result_source->schema,
        'confirm_report_sent.txt',
        $h,
        {
            To => $row->user->email,
        },
        undef,
        $nomail,
        $cobrand,
        $row->lang,
    );
}

sub debug_print {
    my $msg = shift;
    my $id = shift || '';
    $id = "report $id: " if $id;
    print "[] $id$msg\n";
}

1;