Subversion Repositories SmartDukaan

Rev

Rev 37402 | Rev 37408 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
29451 manish 1
package com.smartdukaan.cron.scheduled;
2
 
36420 amit 3
import com.spice.profitmandi.common.web.client.HttpClientFactory;
30209 amit.gupta 4
import com.spice.profitmandi.dao.model.ImeiActivationTimestampModel;
5
import com.spice.profitmandi.dao.repository.fofo.ActivatedImeiRepository;
6
import com.spice.profitmandi.service.inventory.InventoryService;
36420 amit 7
import org.apache.http.NameValuePair;
8
import org.apache.http.client.entity.UrlEncodedFormEntity;
9
import org.apache.http.client.methods.CloseableHttpResponse;
10
import org.apache.http.client.methods.HttpGet;
11
import org.apache.http.client.methods.HttpPost;
12
import org.apache.http.impl.client.BasicCookieStore;
13
import org.apache.http.impl.client.CloseableHttpClient;
14
import org.apache.http.message.BasicNameValuePair;
15
import org.apache.http.util.EntityUtils;
29451 manish 16
import org.apache.logging.log4j.LogManager;
17
import org.apache.logging.log4j.Logger;
18
import org.json.JSONObject;
19
import org.springframework.beans.factory.annotation.Autowired;
20
import org.springframework.stereotype.Component;
36260 amit 21
import org.springframework.transaction.annotation.Propagation;
22
import org.springframework.transaction.annotation.Transactional;
29451 manish 23
 
36420 amit 24
import java.io.IOException;
25
import java.nio.charset.StandardCharsets;
30209 amit.gupta 26
import java.time.LocalDate;
27
import java.time.format.DateTimeFormatter;
30335 amit.gupta 28
import java.util.ArrayList;
29
import java.util.List;
29451 manish 30
 
31
@Component
32
class VivoImeiActivationService {
33
 
33453 amit.gupta 34
    @Autowired
35
    InventoryService inventoryService;
29451 manish 36
 
33453 amit.gupta 37
    @Autowired
38
    CaptchaService captchaService;
29466 amit.gupta 39
 
33453 amit.gupta 40
    @Autowired
41
    ActivatedImeiRepository activatedImeiRepository;
29451 manish 42
 
33453 amit.gupta 43
    private static final Logger LOGGER = LogManager.getLogger(VivoImeiActivationService.class);
29451 manish 44
 
36260 amit 45
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
33453 amit.gupta 46
    public void checkImeiActivation() throws Exception {
47
        if (LocalDate.now().getDayOfMonth() == 1) return;
36253 amit 48
 
49
        List<ImeiActivationTimestampModel> imeisActivationList = activatedImeiRepository
36580 amit 50
                .selectImeiActivationPendingByBrand("Vivo", 4);
36253 amit 51
        LOGGER.info("Secondary imeisActivationList = {}, size = {}", imeisActivationList, imeisActivationList.size());
52
 
53
        processImeis(imeisActivationList);
54
    }
55
 
36260 amit 56
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
36253 amit 57
    public void checkImeiActivationTertiary() throws Exception {
58
        if (LocalDate.now().getDayOfMonth() == 1) return;
59
 
60
        List<ImeiActivationTimestampModel> imeisActivationList = activatedImeiRepository
61
                .selectImeiActivationPendingByBrandTertiary("Vivo", 2);
62
        LOGGER.info("Tertiary imeisActivationList = {}, size = {}", imeisActivationList, imeisActivationList.size());
63
 
64
        processImeis(imeisActivationList);
65
    }
66
 
67
    private void processImeis(List<ImeiActivationTimestampModel> imeisActivationList) throws Exception {
68
        if (imeisActivationList.isEmpty()) return;
69
 
36420 amit 70
        BasicCookieStore cookieStore = new BasicCookieStore();
71
        try (CloseableHttpClient httpClient = HttpClientFactory.apacheHttpWithCookies(cookieStore)) {
72
            // Seed the session cookies
73
            HttpGet seed = new HttpGet("https://www.vivo.com/in/support/IMEI");
74
            try (CloseableHttpResponse seedResponse = httpClient.execute(seed)) {
75
                EntityUtils.consumeQuietly(seedResponse.getEntity());
33453 amit.gupta 76
            }
29466 amit.gupta 77
 
36420 amit 78
            for (ImeiActivationTimestampModel imeiActivationTimestampModel : imeisActivationList) {
37402 amit 79
                Captcha captcha = this.getNewCaptcha(httpClient, cookieStore);
80
                if (captcha == null) {
37393 amit 81
                    // Solver returned something that is not a captcha code (it was down
82
                    // for 46 days returning an nginx 502 page, which used to be submitted
83
                    // to Vivo verbatim). Skip; this IMEI is picked up on the next run.
84
                    continue;
85
                }
37407 amit 86
                this.getImeiActivation(captcha, httpClient, cookieStore, imeiActivationTimestampModel);
33453 amit.gupta 87
            }
36253 amit 88
        }
33453 amit.gupta 89
    }
29451 manish 90
 
37402 amit 91
    /** A solved captcha: the code, plus the image it came from so the verdict can be reported. */
92
    static final class Captcha {
93
        final String code;
94
        final byte[] image;
37407 amit 95
        final long fetchedAt;
29451 manish 96
 
37402 amit 97
        Captcha(String code, byte[] image) {
98
            this.code = code;
99
            this.image = image;
37407 amit 100
            this.fetchedAt = System.currentTimeMillis();
37402 amit 101
        }
102
    }
103
 
104
    public Captcha getNewCaptcha(CloseableHttpClient httpClient, BasicCookieStore cookieStore) throws Exception {
105
        HttpGet request = new HttpGet("https://www.vivo.com/in/support/generatingCodes");
106
 
107
        // Kept in memory rather than written to a shared /tmp/captcha.jpg: the two
108
        // Vivo jobs run concurrently and used to overwrite each other's image. The
109
        // bytes are needed for the verdict report anyway.
110
        byte[] bytes;
36420 amit 111
        try (CloseableHttpResponse response = httpClient.execute(request)) {
37402 amit 112
            bytes = EntityUtils.toByteArray(response.getEntity());
36253 amit 113
        }
29451 manish 114
 
37402 amit 115
        String code = captchaService.getCaptchaCode(bytes);
116
        return code == null ? null : new Captcha(code, bytes);
33453 amit.gupta 117
    }
29451 manish 118
 
37402 amit 119
    public void getImeiActivation(Captcha captcha, CloseableHttpClient httpClient,
37407 amit 120
                                  BasicCookieStore cookieStore,
33453 amit.gupta 121
                                  ImeiActivationTimestampModel vivoImeiAndActivationTimeStampModel) throws Exception {
29451 manish 122
 
33453 amit.gupta 123
        String imei = vivoImeiAndActivationTimeStampModel.getSerialNumber();
29466 amit.gupta 124
 
36420 amit 125
        List<NameValuePair> form = new ArrayList<>();
126
        form.add(new BasicNameValuePair("imei", imei));
37402 amit 127
        form.add(new BasicNameValuePair("code", captcha.code));
36420 amit 128
        HttpPost post = new HttpPost("https://www.vivo.com/in/support/checkCode");
129
        post.setEntity(new UrlEncodedFormEntity(form, StandardCharsets.UTF_8));
29466 amit.gupta 130
 
36253 amit 131
        JSONObject imeiActivationJson;
36420 amit 132
        try (CloseableHttpResponse response2 = httpClient.execute(post)) {
133
            imeiActivationJson = new JSONObject(EntityUtils.toString(response2.getEntity()));
36253 amit 134
        }
135
 
33453 amit.gupta 136
        JSONObject data = imeiActivationJson.getJSONObject("data");
137
        int status = data.getInt("status");
37402 amit 138
 
139
        // Vivo is the only judge of whether our guess was right; status 0 means the
140
        // captcha was wrong. Hand that back so the sample becomes training data.
141
        captchaService.reportVerdict(captcha.image, captcha.code, status != 0);
142
 
33453 amit.gupta 143
        if (status == 0) {
37407 amit 144
            // Vivo refuses ~56% of submissions in production, but reading the stored
145
            // images by hand showed 9 of 10 carried the CORRECT code, and no external
146
            // replication reproduces it (IMEI, user-agent, session reuse, concurrency,
147
            // source IP and submit delay were all measured and ruled out). The old log
148
            // line recorded five words and discarded the evidence, so capture it:
149
            // the code submitted, the imei, how long fetch->submit actually took inside
150
            // this process, the session cookies held at submit time, and Vivo's reply.
151
            LOGGER.info("Vivo rejected captcha code={} imei={} elapsedMs={} cookies={} response={}",
152
                    captcha.code, imei, System.currentTimeMillis() - captcha.fetchedAt,
153
                    cookieStore.getCookies(), imeiActivationJson);
33453 amit.gupta 154
            return;
155
        } else if (status == 2) {
156
            LOGGER.info("Received status 2 for {}, data {}", imei, data);
36253 amit 157
            activatedImeiRepository.saveActivation(imei, null);
33453 amit.gupta 158
            return;
159
        }
29451 manish 160
 
33453 amit.gupta 161
        JSONObject imeiQueryDto = data.getJSONObject("imeiQueryDto");
162
        LOGGER.info("imeiQueryDto - {}", imeiQueryDto);
163
        String dueTimeString = imeiQueryDto.getString("dueTime");
29466 amit.gupta 164
 
36253 amit 165
        if (dueTimeString.equals("")) {
166
            activatedImeiRepository.saveActivation(imei, null);
167
            return;
33453 amit.gupta 168
        }
29469 amit.gupta 169
 
33453 amit.gupta 170
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
171
        dueTimeString = dueTimeString.split(" ")[0];
172
        LocalDate dueDate = LocalDate.parse(dueTimeString, formatter);
30343 amit.gupta 173
 
36253 amit 174
        activatedImeiRepository.saveActivation(imei, dueDate.atStartOfDay().minusYears(1));
175
        LOGGER.info("activatedImei saved for {} with dueDate {}", imei, dueDate.atStartOfDay());
33453 amit.gupta 176
    }
36253 amit 177
}